Merge remote-tracking branch 'upstream/master' into 4.0.0-WIP
Conflicts: build.gradle gradle.properties spring-integration-core/src/main/java/org/springframework/integration/config/RouterFactoryBean.java spring-integration-core/src/test/java/org/springframework/integration/channel/config/DispatchingChannelParserTests.java spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayProxyFactoryBeanTests.java spring-integration-core/src/test/java/org/springframework/integration/router/config/PayloadTypeRouterParserTests.java spring-integration-core/src/test/java/org/springframework/integration/transformer/MessageTransformingChannelInterceptorTests.java spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionFactoryShutDownTests.java spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/TcpNioConnectionReadTests.java spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/DatagramPacketSendingHandlerTests.java spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/MultiClientTests.java spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpChannelAdapterTests.java spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcMessageHandler.java spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcOutboundGateway.java spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapter.java spring-integration-xmpp/src/main/java/org/springframework/integration/xmpp/core/AbstractXmppConnectionAwareEndpoint.java Resolved.
This commit is contained in:
12
build.gradle
12
build.gradle
@@ -59,7 +59,7 @@ subprojects { subproject ->
|
||||
ftpServerVersion = '1.0.6'
|
||||
|
||||
|
||||
springVersionDefault = '4.0.0.BUILD-SNAPSHOT'
|
||||
springVersionDefault = '4.0.0.RELEASE'
|
||||
springVersion = project.hasProperty('springVersion') ? getProperty('springVersion') : springVersionDefault
|
||||
|
||||
springAmqpVersion = '1.2.0.RELEASE'
|
||||
@@ -296,9 +296,11 @@ project('spring-integration-http') {
|
||||
compile ("net.java.dev.rome:rome:1.0.0", optional)
|
||||
testCompile project(":spring-integration-test")
|
||||
|
||||
// suppress deprecation warnings (@SuppressWarnings("deprecation") is not enough for javac)
|
||||
compileJava.options.compilerArgs = ["${xLintArg},-deprecation"]
|
||||
}
|
||||
|
||||
// suppress deprecation warnings (@SuppressWarnings("deprecation") is not enough for javac)
|
||||
compileJava.options.compilerArgs = ["${xLintArg},-deprecation"]
|
||||
|
||||
}
|
||||
|
||||
project('spring-integration-ip') {
|
||||
@@ -309,6 +311,10 @@ project('spring-integration-ip') {
|
||||
runtime project(":spring-integration-stream")
|
||||
testCompile project(":spring-integration-test")
|
||||
}
|
||||
|
||||
// suppress deprecation warnings (@SuppressWarnings("deprecation") is not enough for javac)
|
||||
compileJava.options.compilerArgs = ["${xLintArg},-deprecation"]
|
||||
|
||||
}
|
||||
|
||||
project('spring-integration-jdbc') {
|
||||
|
||||
@@ -15,9 +15,6 @@ package org.springframework.integration.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.router.AbstractMappingMessageRouter;
|
||||
@@ -26,7 +23,6 @@ import org.springframework.integration.router.ExpressionEvaluatingRouter;
|
||||
import org.springframework.integration.router.MethodInvokingRouter;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.core.DestinationResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -41,8 +37,6 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private volatile Map<String, String> channelMappings;
|
||||
|
||||
private volatile MessageChannel defaultOutputChannel;
|
||||
@@ -55,12 +49,6 @@ public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
|
||||
private volatile Boolean ignoreSendFailures;
|
||||
|
||||
private volatile DestinationResolver<MessageChannel> channelResolver;
|
||||
|
||||
public void setChannelResolver(DestinationResolver<MessageChannel> channelResolver) {
|
||||
this.channelResolver = channelResolver;
|
||||
}
|
||||
|
||||
public void setDefaultOutputChannel(MessageChannel defaultOutputChannel) {
|
||||
this.defaultOutputChannel = defaultOutputChannel;
|
||||
}
|
||||
@@ -146,10 +134,6 @@ public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
if (this.resolutionRequired != null) {
|
||||
router.setResolutionRequired(this.resolutionRequired);
|
||||
}
|
||||
if (this.channelResolver != null) {
|
||||
logger.warn("'channel-resolver' attribute has been deprecated in favor of using SpEL via 'expression' attribute");
|
||||
router.setChannelResolver(this.channelResolver);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -160,7 +144,7 @@ public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
private boolean noRouterAttributesProvided() {
|
||||
return this.channelMappings == null && this.defaultOutputChannel == null
|
||||
&& this.timeout == null && this.resolutionRequired == null && this.applySequence == null
|
||||
&& this.ignoreSendFailures == null && this.channelResolver == null;
|
||||
&& this.ignoreSendFailures == null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -119,17 +119,6 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to control the registration process and return the bean name.
|
||||
* If parsing a bean definition whose name can be auto-generated, consider using
|
||||
* {@link #parseConsumer(Element, ParserContext)} instead.
|
||||
* @deprecated Use {@link #doParseAndRegisterConsumer(Element, ParserContext)}
|
||||
*/
|
||||
@Deprecated
|
||||
protected String parseAndRegisterConsumer(Element element, ParserContext parserContext) {
|
||||
return doParseAndRegisterConsumer(element, parserContext).getBeanName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to control the registration process and return the bean name.
|
||||
* If parsing a bean definition whose name can be auto-generated, consider using
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,7 +30,7 @@ import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* Base parser for routers.
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractRouterParser extends AbstractConsumerEndpointParser {
|
||||
@@ -43,7 +43,6 @@ public abstract class AbstractRouterParser extends AbstractConsumerEndpointParse
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "resolution-required");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "apply-sequence");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-send-failures");
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "channel-resolver");
|
||||
BeanDefinition targetRouterBeanDefinition = this.parseRouter(element, parserContext);
|
||||
builder.addPropertyValue("targetObject", targetRouterBeanDefinition);
|
||||
return builder;
|
||||
|
||||
@@ -27,6 +27,9 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.integration.config.ExpressionFactoryBean;
|
||||
import org.springframework.integration.gateway.GatewayMethodMetadata;
|
||||
import org.springframework.integration.gateway.GatewayProxyFactoryBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -105,7 +108,7 @@ public class GatewayParser extends AbstractSimpleBeanDefinitionParser {
|
||||
|
||||
if (hasDefaultHeaders || hasDefaultPayloadExpression) {
|
||||
BeanDefinitionBuilder methodMetadataBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.integration.gateway.GatewayMethodMetadata");
|
||||
GatewayMethodMetadata.class);
|
||||
this.setMethodInvocationHeaders(methodMetadataBuilder, invocationHeaders);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(methodMetadataBuilder, element,
|
||||
"default-payload-expression", "payloadExpression");
|
||||
@@ -120,7 +123,7 @@ public class GatewayParser extends AbstractSimpleBeanDefinitionParser {
|
||||
for (Element methodElement : elements) {
|
||||
String methodName = methodElement.getAttribute("name");
|
||||
BeanDefinitionBuilder methodMetadataBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.integration.gateway.GatewayMethodMetadata");
|
||||
GatewayMethodMetadata.class);
|
||||
methodMetadataBuilder.addPropertyValue("requestChannelName", methodElement.getAttribute("request-channel"));
|
||||
methodMetadataBuilder.addPropertyValue("replyChannelName", methodElement.getAttribute("reply-channel"));
|
||||
methodMetadataBuilder.addPropertyValue("requestTimeout", methodElement.getAttribute("request-timeout"));
|
||||
@@ -151,11 +154,11 @@ public class GatewayParser extends AbstractSimpleBeanDefinitionParser {
|
||||
}
|
||||
RootBeanDefinition expressionDef = null;
|
||||
if (hasValue) {
|
||||
expressionDef = new RootBeanDefinition("org.springframework.expression.common.LiteralExpression");
|
||||
expressionDef = new RootBeanDefinition(LiteralExpression.class);
|
||||
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(headerValue);
|
||||
}
|
||||
else if (hasExpression) {
|
||||
expressionDef = new RootBeanDefinition("org.springframework.integration.config.ExpressionFactoryBean");
|
||||
expressionDef = new RootBeanDefinition(ExpressionFactoryBean.class);
|
||||
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(headerExpression);
|
||||
}
|
||||
if (expressionDef != null) {
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.integration.config.xml;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
@@ -43,8 +41,6 @@ import org.springframework.util.xml.DomUtils;
|
||||
*/
|
||||
public class PointToPointChannelParser extends AbstractChannelParser {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder buildBeanDefinition(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = null;
|
||||
@@ -83,19 +79,10 @@ public class PointToPointChannelParser extends AbstractChannelParser {
|
||||
|
||||
Element dispatcherElement = DomUtils.getChildElementByTagName(element, "dispatcher");
|
||||
|
||||
// check for the dispatcher attribute (deprecated)
|
||||
String dispatcherAttribute = element.getAttribute("dispatcher");
|
||||
boolean hasDispatcherAttribute = StringUtils.hasText(dispatcherAttribute);
|
||||
if (hasDispatcherAttribute && logger.isWarnEnabled()) {
|
||||
logger.warn("The 'dispatcher' attribute on the 'channel' element is deprecated. "
|
||||
+ "Please use the 'dispatcher' sub-element instead.");
|
||||
}
|
||||
|
||||
// verify that a dispatcher is not provided if a queue sub-element exists
|
||||
if (queueElement != null && (dispatcherElement != null || hasDispatcherAttribute)) {
|
||||
if (queueElement != null && dispatcherElement != null) {
|
||||
parserContext.getReaderContext().error(
|
||||
"The 'dispatcher' attribute or sub-element " + "and any queue sub-element are mutually exclusive.",
|
||||
element);
|
||||
"The 'dispatcher' sub-element and any queue sub-element are mutually exclusive.", element);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -103,24 +90,7 @@ public class PointToPointChannelParser extends AbstractChannelParser {
|
||||
return builder;
|
||||
}
|
||||
|
||||
if (dispatcherElement != null && hasDispatcherAttribute) {
|
||||
parserContext.getReaderContext().error(
|
||||
"The 'dispatcher' attribute and 'dispatcher' "
|
||||
+ "sub-element are mutually exclusive. NOTE: the attribute is DEPRECATED. "
|
||||
+ "Please use the dispatcher sub-element instead.", element);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (hasDispatcherAttribute) {
|
||||
// this attribute is deprecated, but if set, we need to create a DirectChannel
|
||||
// without any LoadBalancerStrategy and the failover flag set to true (default).
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(DirectChannel.class);
|
||||
if ("failover".equals(dispatcherAttribute)) {
|
||||
// round-robin dispatcher is used by default, the "failover" value simply disables it
|
||||
builder.addConstructorArgValue(null);
|
||||
}
|
||||
}
|
||||
else if (dispatcherElement == null) {
|
||||
if (dispatcherElement == null) {
|
||||
// configure the default DirectChannel with a RoundRobinLoadBalancingStrategy
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(DirectChannel.class);
|
||||
}
|
||||
@@ -134,12 +104,22 @@ public class PointToPointChannelParser extends AbstractChannelParser {
|
||||
else {
|
||||
builder = BeanDefinitionBuilder.genericBeanDefinition(DirectChannel.class);
|
||||
}
|
||||
// unless the 'load-balancer' attribute is explicitly set to 'none',
|
||||
// unless the 'load-balancer' attribute is explicitly set to 'none' or 'load-balancer-ref' is explicitly configured,
|
||||
// configure the default RoundRobinLoadBalancingStrategy
|
||||
String loadBalancer = dispatcherElement.getAttribute("load-balancer");
|
||||
if ("none".equals(loadBalancer)) {
|
||||
builder.addConstructorArgValue(null);
|
||||
String loadBalancerRef = dispatcherElement.getAttribute("load-balancer-ref");
|
||||
if (StringUtils.hasText(loadBalancer) && StringUtils.hasText(loadBalancerRef)){
|
||||
parserContext.getReaderContext().error("'load-balancer' and 'load-balancer-ref' are mutually exclusive", element);
|
||||
}
|
||||
if (StringUtils.hasText(loadBalancerRef)){
|
||||
builder.addConstructorArgReference(loadBalancerRef);
|
||||
}
|
||||
else {
|
||||
if ("none".equals(loadBalancer)) {
|
||||
builder.addConstructorArgValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, dispatcherElement, "failover");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, dispatcherElement, "max-subscribers");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,11 +27,12 @@ import org.springframework.expression.Expression;
|
||||
* <p>
|
||||
* The sub-element of a <gateway> element would look like this:
|
||||
* <method name="echo" request-channel="inputA" reply-timeout="2" request-timeout="200"/>
|
||||
*
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
class GatewayMethodMetadata {
|
||||
public class GatewayMethodMetadata {
|
||||
|
||||
private volatile String payloadExpression;
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public class JsonPropertyAccessor implements PropertyAccessor {
|
||||
/**
|
||||
* The kind of types this can work with.
|
||||
*/
|
||||
private static final Class<?>[] SUPPORTED_CLASSES = new Class[] { String.class, ToStringFriendlyJsonNode.class,
|
||||
private static final Class<?>[] SUPPORTED_CLASSES = new Class<?>[] { String.class, ToStringFriendlyJsonNode.class,
|
||||
ObjectNode.class, ArrayNode.class };
|
||||
|
||||
// Note: ObjectMapper is thread-safe
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,9 +24,13 @@ import org.springframework.integration.channel.interceptor.ChannelInterceptorAda
|
||||
/**
|
||||
* A {@link ChannelInterceptor} which invokes a {@link Transformer}
|
||||
* when either sending-to or receiving-from a channel.
|
||||
*
|
||||
*
|
||||
* @deprecated It is not generally recommended to perform functions
|
||||
* such as transformation in a channel interceptor.
|
||||
*
|
||||
* @author Jonas Partner
|
||||
*/
|
||||
@Deprecated
|
||||
public class MessageTransformingChannelInterceptor extends ChannelInterceptorAdapter {
|
||||
|
||||
private final Transformer transformer;
|
||||
|
||||
@@ -286,12 +286,25 @@
|
||||
(i.e. one without a queue).
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="load-balancer-ref" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.dispatcher.LoadBalancingStrategy" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
A reference to a bean that implements the 'org.springframework.integration.dispatcher.LoadBalancingStrategy'.
|
||||
This attribute is mutually exclusive with 'load-balancer'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="load-balancer">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a load-balancing strategy for the channel's dispatcher.
|
||||
The default is a round-robin load
|
||||
balancer.
|
||||
The default is a round-robin load balancer.
|
||||
This attribute is mutually exclusive with 'load-balancer-ref'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
@@ -485,27 +498,6 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="dispatcher">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
This attribute is DEPRECATED. Please use the dispatcher sub-element
|
||||
instead.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="failover">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Enables failover, but disables load-balancing.
|
||||
See the dispatcher sub-element for more
|
||||
information.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:element name="gateway">
|
||||
@@ -3195,20 +3187,6 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="channel-resolver" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
[DEPRECATED]Provides a reference to a ChannelResolver that resolves the return value
|
||||
to the name of a MessageChannel within the application context. If none
|
||||
is provided, the return value is expected to match a channel name exactly.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.support.channel.ChannelResolver" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="ignore-send-failures">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
@@ -29,6 +29,7 @@ import static org.junit.Assert.assertTrue;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -36,6 +37,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.PublishSubscribeChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.channel.interceptor.ChannelInterceptorAdapter;
|
||||
import org.springframework.integration.config.TestChannelInterceptor;
|
||||
import org.springframework.integration.dispatcher.RoundRobinLoadBalancingStrategy;
|
||||
import org.springframework.integration.dispatcher.UnicastingDispatcher;
|
||||
@@ -90,7 +92,7 @@ public class ChannelParserTests {
|
||||
public void channelWithFailoverDispatcherAttribute() throws Exception {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("channelParserTests.xml", this
|
||||
.getClass());
|
||||
MessageChannel channel = (MessageChannel) context.getBean("channelWithFailoverAttribute");
|
||||
MessageChannel channel = (MessageChannel) context.getBean("channelWithFailover");
|
||||
assertEquals(DirectChannel.class, channel.getClass());
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(channel);
|
||||
Object dispatcher = accessor.getPropertyValue("dispatcher");
|
||||
@@ -259,4 +261,13 @@ public class ChannelParserTests {
|
||||
assertTrue(threwException);
|
||||
}
|
||||
|
||||
public static class TestInterceptor extends ChannelInterceptorAdapter {
|
||||
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
return MessageBuilder.withPayload(message.getPayload().toString().toUpperCase()).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
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">
|
||||
|
||||
<int:channel id="myChannel">
|
||||
<int:dispatcher load-balancer-ref="lb2" load-balancer="round-robin"/>
|
||||
</int:channel>
|
||||
|
||||
<bean id="lb"
|
||||
class="org.springframework.integration.channel.config.DispatchingChannelParserTests.SampleLoadBalancingStrategy"/>
|
||||
</beans>
|
||||
@@ -1,13 +1,12 @@
|
||||
<?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
|
||||
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="dispatcherAttribute" dispatcher="failover"/>
|
||||
|
||||
<channel id="taskExecutorOnly">
|
||||
<dispatcher task-executor="taskExecutor"/>
|
||||
</channel>
|
||||
@@ -32,7 +31,13 @@
|
||||
<dispatcher load-balancer="round-robin" task-executor="taskExecutor"/>
|
||||
</channel>
|
||||
|
||||
<beans:bean id="taskExecutor"
|
||||
class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
|
||||
<channel id="lbRefChannel">
|
||||
<dispatcher load-balancer-ref="lb"/>
|
||||
</channel>
|
||||
|
||||
<beans:bean id="taskExecutor"
|
||||
class="org.springframework.core.task.SimpleAsyncTaskExecutor"/>
|
||||
|
||||
<beans:bean id="lb"
|
||||
class="org.springframework.integration.channel.config.DispatchingChannelParserTests.SampleLoadBalancingStrategy"/>
|
||||
</beans:beans>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,28 +20,37 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.ExecutorChannel;
|
||||
import org.springframework.integration.dispatcher.LoadBalancingStrategy;
|
||||
import org.springframework.integration.dispatcher.RoundRobinLoadBalancingStrategy;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.util.ErrorHandlingTaskExecutor;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 1.0.3
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@@ -54,20 +63,6 @@ public class DispatchingChannelParserTests {
|
||||
@Autowired
|
||||
private Map<String, MessageChannel> channels;
|
||||
|
||||
|
||||
@Test(expected = FatalBeanException.class)
|
||||
public void dispatcherAttributeAndSubElement() {
|
||||
new ClassPathXmlApplicationContext("dispatcherAttributeAndSubElement.xml", this.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dispatcherAttribute() {
|
||||
MessageChannel channel = channels.get("dispatcherAttribute");
|
||||
assertEquals(DirectChannel.class, channel.getClass());
|
||||
assertTrue((Boolean) getDispatcherProperty("failover", channel));
|
||||
assertNull(getDispatcherProperty("loadBalancingStrategy", channel));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void taskExecutorOnly() {
|
||||
MessageChannel channel = channels.get("taskExecutorOnly");
|
||||
@@ -132,6 +127,24 @@ public class DispatchingChannelParserTests {
|
||||
new DirectFieldAccessor(executor).getPropertyValue("executor"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadBalancerRef() {
|
||||
MessageChannel channel = channels.get("lbRefChannel");
|
||||
LoadBalancingStrategy lbStrategy = TestUtils.getPropertyValue(channel, "dispatcher.loadBalancingStrategy", LoadBalancingStrategy.class);
|
||||
assertTrue(lbStrategy instanceof SampleLoadBalancingStrategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadBalancerRefFailWithLoadBalancer() {
|
||||
|
||||
try {
|
||||
new ClassPathXmlApplicationContext("ChannelWithLoadBalancerRef-fail-config.xml", this.getClass());
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
assertThat(e.getMessage(), Matchers.containsString("'load-balancer' and 'load-balancer-ref' are mutually exclusive"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static Object getDispatcherProperty(String propertyName, MessageChannel channel) {
|
||||
return new DirectFieldAccessor(
|
||||
@@ -139,4 +152,11 @@ public class DispatchingChannelParserTests {
|
||||
.getPropertyValue(propertyName);
|
||||
}
|
||||
|
||||
public static class SampleLoadBalancingStrategy implements LoadBalancingStrategy {
|
||||
|
||||
@Override
|
||||
public Iterator<MessageHandler> getHandlerIterator(Message<?> message, Collection<MessageHandler> handlers) {
|
||||
return handlers.iterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,7 @@
|
||||
<channel id="channelWithInterceptorInnerBean">
|
||||
<queue capacity="5"/>
|
||||
<interceptors>
|
||||
<beans:bean class="org.springframework.integration.transformer.MessageTransformingChannelInterceptor">
|
||||
<beans:constructor-arg>
|
||||
<beans:bean class="org.springframework.integration.channel.config.TestTransformer"/>
|
||||
</beans:constructor-arg>
|
||||
</beans:bean>
|
||||
<beans:bean class="org.springframework.integration.channel.config.ChannelParserTests$TestInterceptor"/>
|
||||
</interceptors>
|
||||
</channel>
|
||||
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
</channel>
|
||||
|
||||
<channel id="defaultChannel" />
|
||||
|
||||
<channel id="channelWithFailoverAttribute" dispatcher="failover"/>
|
||||
|
||||
<channel id="channelWithFailover">
|
||||
<dispatcher failover="true" load-balancer="none"/>
|
||||
</channel>
|
||||
|
||||
<channel id="channelWithCustomQueue">
|
||||
<queue ref="customQueue"/>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<?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="dispatcherAttributeAndSubElement" dispatcher="failover">
|
||||
<dispatcher/>
|
||||
</channel>
|
||||
|
||||
</beans:beans>
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package org.springframework.integration.gateway;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -37,14 +40,16 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
@@ -77,6 +82,7 @@ public class GatewayProxyFactoryBeanTests {
|
||||
startResponder(requestChannel);
|
||||
GenericConversionService cs = new DefaultConversionService();
|
||||
Converter<String, byte[]> stringToByteConverter = new Converter<String, byte[]>() {
|
||||
@Override
|
||||
public byte[] convert(String source) {
|
||||
return source.getBytes();
|
||||
}
|
||||
@@ -134,6 +140,7 @@ public class GatewayProxyFactoryBeanTests {
|
||||
public void testRequestReplyWithTypeConversion() throws Exception {
|
||||
final QueueChannel requestChannel = new QueueChannel();
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Message<?> input = requestChannel.receive();
|
||||
GenericMessage<String> reply = new GenericMessage<String>(input.getPayload() + "456");
|
||||
@@ -184,6 +191,7 @@ public class GatewayProxyFactoryBeanTests {
|
||||
for (int i = 0; i < numRequests; i++) {
|
||||
final int count = i;
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// add some randomness to the ordering of requests
|
||||
try {
|
||||
@@ -240,6 +248,7 @@ public class GatewayProxyFactoryBeanTests {
|
||||
public void testMessageAsReturnValue() throws Exception {
|
||||
final QueueChannel requestChannel = new QueueChannel();
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Message<?> input = requestChannel.receive();
|
||||
GenericMessage<String> reply = new GenericMessage<String>(input.getPayload() + "bar");
|
||||
@@ -291,6 +300,7 @@ public class GatewayProxyFactoryBeanTests {
|
||||
GatewayProxyFactoryBean proxyFactory = new GatewayProxyFactoryBean();
|
||||
DirectChannel channel = new DirectChannel();
|
||||
EventDrivenConsumer consumer = new EventDrivenConsumer(channel, new MessageHandler() {
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) {
|
||||
Method method = ReflectionUtils.findMethod(
|
||||
GatewayProxyFactoryBeanTests.class, "throwTestException");
|
||||
@@ -310,6 +320,7 @@ public class GatewayProxyFactoryBeanTests {
|
||||
|
||||
private static void startResponder(final PollableChannel requestChannel) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Message<?> input = requestChannel.receive();
|
||||
GenericMessage<String> reply = new GenericMessage<String>(input.getPayload() + "bar");
|
||||
@@ -318,6 +329,26 @@ public class GatewayProxyFactoryBeanTests {
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgrammaticWiring() throws Exception {
|
||||
GatewayProxyFactoryBean gpfb = new GatewayProxyFactoryBean();
|
||||
gpfb.setBeanFactory(mock(BeanFactory.class));
|
||||
gpfb.setServiceInterface(TestEchoService.class);
|
||||
QueueChannel drc = new QueueChannel();
|
||||
gpfb.setDefaultRequestChannel(drc);
|
||||
gpfb.setDefaultReplyTimeout(0);
|
||||
GatewayMethodMetadata meta = new GatewayMethodMetadata();
|
||||
meta.setHeaderExpressions(Collections. <String, Expression> singletonMap("foo", new LiteralExpression("bar")));
|
||||
gpfb.setGlobalMethodMetadata(meta);
|
||||
gpfb.afterPropertiesSet();
|
||||
((TestEchoService) gpfb.getObject()).echo("foo");
|
||||
Message<?> message = drc.receive(0);
|
||||
assertNotNull(message);
|
||||
String bar = (String) message.getHeaders().get("foo");
|
||||
assertNotNull(bar);
|
||||
assertThat(bar, equalTo("bar"));
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testHistory() throws Exception {
|
||||
// GenericApplicationContext context = new GenericApplicationContext();
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
<channel id="channel4">
|
||||
<queue capacity="1" />
|
||||
</channel>
|
||||
|
||||
|
||||
<channel id="routingChannel" />
|
||||
<payload-type-router id="routerWithChannelResolver" input-channel="routingChannel" channel-resolver="cr">
|
||||
|
||||
<payload-type-router id="router" input-channel="routingChannel">
|
||||
<mapping type="java.lang.String" channel="channel1" />
|
||||
<mapping type="java.lang.Integer" channel="channel2" />
|
||||
<mapping type="java.lang.Number[]" channel="channel3" />
|
||||
@@ -32,5 +33,4 @@
|
||||
service-interface="org.springframework.integration.router.config.PayloadTypeRouterParserTests$TestService"
|
||||
default-request-channel="routingChannel" />
|
||||
|
||||
<beans:bean id="cr" class="org.springframework.integration.router.config.PayloadTypeRouterParserTests.MyChannelResolver"/>
|
||||
</beans:beans>
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
package org.springframework.integration.router.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
@@ -26,16 +24,12 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -73,9 +67,6 @@ public class PayloadTypeRouterParserTests {
|
||||
assertTrue(chanel2.receive(100).getPayload() instanceof Integer);
|
||||
assertTrue(chanel3.receive(100).getPayload().getClass().isArray());
|
||||
assertTrue(chanel4.receive(100).getPayload().getClass().isArray());
|
||||
|
||||
EventDrivenConsumer edc = context.getBean("routerWithChannelResolver", EventDrivenConsumer.class);
|
||||
assertEquals(context.getBean("cr"), TestUtils.getPropertyValue(edc, "handler.channelResolver"));
|
||||
}
|
||||
|
||||
@Test(expected=BeanDefinitionStoreException.class)
|
||||
@@ -119,10 +110,4 @@ public class PayloadTypeRouterParserTests {
|
||||
public void foo(Message<?> message);
|
||||
}
|
||||
|
||||
public static class MyChannelResolver extends BeanFactoryChannelResolver {
|
||||
|
||||
MyChannelResolver() {
|
||||
super(mock(BeanFactory.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +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.transformer;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
/**
|
||||
* @author Jonas Partner
|
||||
*/
|
||||
public class MessageTransformingChannelInterceptorTests {
|
||||
|
||||
private QueueChannel channel;
|
||||
|
||||
private GenericMessage<String> message;
|
||||
|
||||
private TestTransformer transformer;
|
||||
|
||||
private MessageTransformingChannelInterceptor channelInterceptor;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
channel = new QueueChannel();
|
||||
message = new GenericMessage<String>("test");
|
||||
transformer = new TestTransformer();
|
||||
channelInterceptor = new MessageTransformingChannelInterceptor(transformer);
|
||||
channel.addInterceptor(channelInterceptor);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTransformOnReceive() {
|
||||
channelInterceptor.setTransformOnSend(false);
|
||||
channel.send(message);
|
||||
assertFalse("Transformer incorrectly invoked on send", transformer.invoked);
|
||||
Message<?> msg = channel.receive(1);
|
||||
assertEquals("Wrong message", message, msg);
|
||||
assertTrue("Transformer not invoked on receive", transformer.invoked);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformOnSend() {
|
||||
channelInterceptor.setTransformOnSend(true);
|
||||
channel.send(message);
|
||||
assertTrue("Transformer not invoked on send", transformer.invoked);
|
||||
Message<?> msg = channel.receive(1);
|
||||
assertEquals("Wrong message", message, msg);
|
||||
assertEquals("Transformer invoked on receive", 1, transformer.invokedCount);
|
||||
}
|
||||
|
||||
|
||||
private static class TestTransformer implements Transformer {
|
||||
|
||||
boolean invoked = false;
|
||||
|
||||
int invokedCount = 0;
|
||||
|
||||
public Message<?> transform(Message<?> message) {
|
||||
invoked = true;
|
||||
invokedCount++;
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -110,11 +110,11 @@ public class FileTailInboundChannelAdapterFactoryBean extends AbstractFactoryBea
|
||||
this.outputChannel = outputChannel;
|
||||
}
|
||||
|
||||
public void setAutoStartup(Boolean autoStartup) {
|
||||
public void setAutoStartup(boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
}
|
||||
|
||||
public void setPhase(Integer phase) {
|
||||
public void setPhase(int phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
|
||||
@@ -211,6 +211,7 @@ public class DefaultHttpHeaderMapper implements HeaderMapper<HttpHeaders>, BeanF
|
||||
AGE,
|
||||
ALLOW,
|
||||
CACHE_CONTROL,
|
||||
CONNECTION,
|
||||
CONTENT_ENCODING,
|
||||
CONTENT_LANGUAGE,
|
||||
CONTENT_LENGTH,
|
||||
|
||||
@@ -50,6 +50,8 @@ import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
@@ -98,6 +100,7 @@ public class HttpProxyScenarioTests {
|
||||
|
||||
request.addHeader("If-Modified-Since", ifModifiedSinceValue);
|
||||
request.addHeader("If-Unmodified-Since", ifUnmodifiedSinceValue);
|
||||
request.addHeader("Connection", "Keep-Alive");
|
||||
|
||||
Object handler = this.handlerMapping.getHandler(request).getHandler();
|
||||
assertNotNull(handler);
|
||||
@@ -116,7 +119,11 @@ public class HttpProxyScenarioTests {
|
||||
HttpHeaders httpHeaders = httpEntity.getHeaders();
|
||||
assertEquals(ifModifiedSince, httpHeaders.getIfNotModifiedSince());
|
||||
assertEquals(ifUnmodifiedSinceValue, httpHeaders.getFirst("If-Unmodified-Since"));
|
||||
return new ResponseEntity<Object>(httpEntity.getHeaders(), HttpStatus.OK);
|
||||
assertEquals("Keep-Alive", httpHeaders.getFirst("Connection"));
|
||||
|
||||
MultiValueMap<String, String> responseHeaders = new LinkedMultiValueMap<String, String>(httpHeaders);
|
||||
responseHeaders.set("Connection", "close");
|
||||
return new ResponseEntity<Object>(responseHeaders, HttpStatus.OK);
|
||||
}
|
||||
}).when(template).exchange(Mockito.any(URI.class), Mockito.any(HttpMethod.class),
|
||||
Mockito.any(HttpEntity.class), (Class<?>) Mockito.any(Class.class));
|
||||
@@ -131,6 +138,7 @@ public class HttpProxyScenarioTests {
|
||||
|
||||
assertNull(response.getHeaderValue("If-Modified-Since"));
|
||||
assertNull(response.getHeaderValue("If-Unmodified-Since"));
|
||||
assertEquals("close", response.getHeaderValue("Connection"));
|
||||
|
||||
Message<?> message = this.checkHeadersChannel.receive(2000);
|
||||
MessageHeaders headers = message.getHeaders();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,10 +17,12 @@
|
||||
package org.springframework.integration.ip;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import org.springframework.integration.endpoint.MessageProducerSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for inbound TCP/UDP Channel Adapters.
|
||||
@@ -48,6 +50,8 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
|
||||
|
||||
private volatile Executor taskExecutor;
|
||||
|
||||
private volatile boolean taskExecutorSet;
|
||||
|
||||
private volatile int poolSize = 5;
|
||||
|
||||
|
||||
@@ -63,6 +67,7 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoTimeout(int soTimeout) {
|
||||
this.soTimeout = soTimeout;
|
||||
}
|
||||
@@ -74,6 +79,7 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
|
||||
return soTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoReceiveBufferSize(int soReceiveBufferSize) {
|
||||
this.soReceiveBufferSize = soReceiveBufferSize;
|
||||
}
|
||||
@@ -117,6 +123,7 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
|
||||
protected void checkTaskExecutor(final String threadName) {
|
||||
if (this.active && this.taskExecutor == null) {
|
||||
Executor executor = Executors.newFixedThreadPool(this.poolSize, new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable runner) {
|
||||
Thread thread = new Thread(runner);
|
||||
thread.setName(threadName);
|
||||
@@ -131,6 +138,10 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
|
||||
@Override
|
||||
protected void doStop() {
|
||||
this.active = false;
|
||||
if (!this.taskExecutorSet && this.taskExecutor != null) {
|
||||
((ExecutorService) this.taskExecutor).shutdown();
|
||||
this.taskExecutor = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isListening() {
|
||||
@@ -148,6 +159,7 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
|
||||
return localAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocalAddress(String localAddress) {
|
||||
this.localAddress = localAddress;
|
||||
}
|
||||
@@ -157,7 +169,9 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
|
||||
}
|
||||
|
||||
public void setTaskExecutor(Executor taskExecutor) {
|
||||
Assert.notNull(taskExecutor, "'taskExecutor' cannot be null");
|
||||
this.taskExecutor = taskExecutor;
|
||||
this.taskExecutorSet = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,7 @@ import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.integration.handler.AbstractMessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -30,7 +31,8 @@ import org.springframework.util.Assert;
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractInternetProtocolSendingMessageHandler extends AbstractMessageHandler implements CommonSocketOptions {
|
||||
public abstract class AbstractInternetProtocolSendingMessageHandler extends AbstractMessageHandler implements CommonSocketOptions,
|
||||
Lifecycle {
|
||||
|
||||
private final SocketAddress destinationAddress;
|
||||
|
||||
@@ -42,6 +44,8 @@ public abstract class AbstractInternetProtocolSendingMessageHandler extends Abst
|
||||
|
||||
private volatile int soTimeout = -1;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
public AbstractInternetProtocolSendingMessageHandler(String host, int port) {
|
||||
Assert.notNull(host, "host must not be null");
|
||||
this.destinationAddress = new InetSocketAddress(host, port);
|
||||
@@ -55,6 +59,7 @@ public abstract class AbstractInternetProtocolSendingMessageHandler extends Abst
|
||||
* @see DatagramSocket#setSoTimeout(int)
|
||||
* @param timeout
|
||||
*/
|
||||
@Override
|
||||
public void setSoTimeout(int timeout) {
|
||||
this.soTimeout = timeout;
|
||||
}
|
||||
@@ -64,6 +69,7 @@ public abstract class AbstractInternetProtocolSendingMessageHandler extends Abst
|
||||
* @see DatagramSocket#setReceiveBufferSize(int)
|
||||
* @param size
|
||||
*/
|
||||
@Override
|
||||
public void setSoReceiveBufferSize(int size) {
|
||||
}
|
||||
|
||||
@@ -72,6 +78,7 @@ public abstract class AbstractInternetProtocolSendingMessageHandler extends Abst
|
||||
* @see DatagramSocket#setSendBufferSize(int)
|
||||
* @param size
|
||||
*/
|
||||
@Override
|
||||
public void setSoSendBufferSize(int size) {
|
||||
this.soSendBufferSize = size;
|
||||
}
|
||||
@@ -115,4 +122,31 @@ public abstract class AbstractInternetProtocolSendingMessageHandler extends Abst
|
||||
return soSendBufferSize;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void start() {
|
||||
if (!this.running) {
|
||||
this.doStart();
|
||||
this.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void doStart();
|
||||
|
||||
@Override
|
||||
public synchronized void stop() {
|
||||
if (this.running) {
|
||||
this.doStop();
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void doStop();
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -410,8 +410,10 @@ public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the server.
|
||||
* Closes the factory.
|
||||
* @deprecated As of 3.0; use {@link #stop()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract void close();
|
||||
|
||||
@Override
|
||||
|
||||
@@ -44,6 +44,7 @@ public class CachingClientConnectionFactory extends AbstractClientConnectionFact
|
||||
this.targetConnectionFactory = target;
|
||||
pool = new SimplePool<TcpConnectionSupport>(poolSize, new SimplePool.PoolItemCallback<TcpConnectionSupport>() {
|
||||
|
||||
@Override
|
||||
public TcpConnectionSupport createForPool() {
|
||||
try {
|
||||
return targetConnectionFactory.getConnection();
|
||||
@@ -52,10 +53,12 @@ public class CachingClientConnectionFactory extends AbstractClientConnectionFact
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStale(TcpConnectionSupport connection) {
|
||||
return !connection.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removedFromPool(TcpConnectionSupport connection) {
|
||||
connection.close();
|
||||
}
|
||||
@@ -167,6 +170,7 @@ public class CachingClientConnectionFactory extends AbstractClientConnectionFact
|
||||
return targetConnectionFactory.isRunning();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void close() {
|
||||
targetConnectionFactory.close();
|
||||
@@ -317,6 +321,7 @@ public class CachingClientConnectionFactory extends AbstractClientConnectionFact
|
||||
public void registerListener(TcpListener listener) {
|
||||
super.registerListener(listener);
|
||||
targetConnectionFactory.registerListener(new TcpListener() {
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
if (!(message instanceof ErrorMessage)) {
|
||||
throw new UnsupportedOperationException("This should never be called");
|
||||
|
||||
@@ -82,6 +82,7 @@ public class FailoverClientConnectionFactory extends AbstractClientConnectionFac
|
||||
super.registerListener(listener);
|
||||
for (AbstractClientConnectionFactory factory : this.factories) {
|
||||
factory.registerListener(new TcpListener() {
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
if (!(message instanceof ErrorMessage)) {
|
||||
throw new UnsupportedOperationException("This should never be called");
|
||||
@@ -112,6 +113,7 @@ public class FailoverClientConnectionFactory extends AbstractClientConnectionFac
|
||||
return failoverTcpConnection;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void close() {
|
||||
for (AbstractClientConnectionFactory factory : this.factories) {
|
||||
@@ -229,6 +231,7 @@ public class FailoverClientConnectionFactory extends AbstractClientConnectionFac
|
||||
this.open = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return this.open;
|
||||
}
|
||||
@@ -238,6 +241,7 @@ public class FailoverClientConnectionFactory extends AbstractClientConnectionFac
|
||||
* send to a new connection obtained from {@link #findAConnection()}.
|
||||
* If send fails on a connection from every factory, we give up.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void send(Message<?> message) throws Exception {
|
||||
boolean success = false;
|
||||
AbstractClientConnectionFactory lastFactoryToTry = this.currentFactory;
|
||||
@@ -268,10 +272,12 @@ public class FailoverClientConnectionFactory extends AbstractClientConnectionFac
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPayload() throws Exception {
|
||||
return this.delegate.getPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
throw new UnsupportedOperationException("Not supported on FailoverTcpConnection");
|
||||
}
|
||||
@@ -286,10 +292,12 @@ public class FailoverClientConnectionFactory extends AbstractClientConnectionFac
|
||||
return this.delegate.getHostAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return this.delegate.getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDeserializerStateKey() {
|
||||
return this.delegate.getDeserializerStateKey();
|
||||
}
|
||||
@@ -350,6 +358,7 @@ public class FailoverClientConnectionFactory extends AbstractClientConnectionFac
|
||||
* the actual connectionId in another header for convenience and tracing
|
||||
* purposes.
|
||||
*/
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
if (this.delegate.getConnectionId().equals(message.getHeaders().get(IpHeaders.CONNECTION_ID))) {
|
||||
MessageBuilder<?> messageBuilder = MessageBuilder.fromMessage(message)
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.ClosedSelectorException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.SocketChannel;
|
||||
@@ -111,10 +112,16 @@ public class TcpNioClientConnectionFactory extends
|
||||
this.tcpNioConnectionSupport = tcpNioSupport;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.selector != null) {
|
||||
this.selector.wakeup();
|
||||
try {
|
||||
this.selector.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Error closing selector", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +136,7 @@ public class TcpNioClientConnectionFactory extends
|
||||
super.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Read selector running for connections to " + this.getHost() + ":" + this.getPort());
|
||||
@@ -141,7 +149,8 @@ public class TcpNioClientConnectionFactory extends
|
||||
int selectionCount = 0;
|
||||
try {
|
||||
selectionCount = selector.select(soTimeout < 0 ? 0 : soTimeout);
|
||||
} catch (CancelledKeyException cke) {
|
||||
}
|
||||
catch (CancelledKeyException cke) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("CancelledKeyException during Selector.select()");
|
||||
}
|
||||
@@ -149,7 +158,8 @@ public class TcpNioClientConnectionFactory extends
|
||||
while ((newChannel = newChannels.poll()) != null) {
|
||||
try {
|
||||
newChannel.register(this.selector, SelectionKey.OP_READ, channelMap.get(newChannel));
|
||||
} catch (ClosedChannelException cce) {
|
||||
}
|
||||
catch (ClosedChannelException cce) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Channel closed before registering with selector for reading");
|
||||
}
|
||||
@@ -157,7 +167,13 @@ public class TcpNioClientConnectionFactory extends
|
||||
}
|
||||
this.processNioSelections(selectionCount, selector, null, this.channelMap);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (ClosedSelectorException cse) {
|
||||
if (this.isActive()) {
|
||||
logger.error("Selector closed", cse);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Exception in read selector thread", e);
|
||||
this.setActive(false);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.ClosedSelectorException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
@@ -67,6 +68,7 @@ public class TcpNioServerConnectionFactory extends AbstractServerConnectionFacto
|
||||
* connection {@link TcpConnection#run()} using the task executor.
|
||||
* I/O errors on the server socket/channel are logged and the factory is stopped.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
if (this.getListener() == null) {
|
||||
logger.info("No listener bound to server connection factory; will not read; exiting...");
|
||||
@@ -83,7 +85,8 @@ public class TcpNioServerConnectionFactory extends AbstractServerConnectionFacto
|
||||
if (this.getLocalAddress() == null) {
|
||||
this.serverChannel.socket().bind(new InetSocketAddress(port),
|
||||
Math.abs(this.getBacklog()));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
InetAddress whichNic = InetAddress.getByName(this.getLocalAddress());
|
||||
this.serverChannel.socket().bind(new InetSocketAddress(whichNic, port),
|
||||
Math.abs(this.getBacklog()));
|
||||
@@ -94,7 +97,8 @@ public class TcpNioServerConnectionFactory extends AbstractServerConnectionFacto
|
||||
this.selector = selector;
|
||||
doSelect(this.serverChannel, selector);
|
||||
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
this.close();
|
||||
if (this.isActive()) {
|
||||
logger.error("Error on ServerSocketChannel", e);
|
||||
@@ -127,12 +131,19 @@ public class TcpNioServerConnectionFactory extends AbstractServerConnectionFacto
|
||||
int selectionCount = 0;
|
||||
try {
|
||||
selectionCount = selector.select(soTimeout < 0 ? 0 : soTimeout);
|
||||
} catch (CancelledKeyException cke) {
|
||||
this.processNioSelections(selectionCount, selector, server, this.channelMap);
|
||||
}
|
||||
catch (CancelledKeyException cke) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("CancelledKeyException during Selector.select()");
|
||||
}
|
||||
}
|
||||
this.processNioSelections(selectionCount, selector, server, this.channelMap);
|
||||
catch (ClosedSelectorException cse) {
|
||||
if (this.isActive()) {
|
||||
logger.error("Selector closed", cse);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,14 +206,20 @@ public class TcpNioServerConnectionFactory extends AbstractServerConnectionFacto
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.selector != null) {
|
||||
this.selector.wakeup();
|
||||
try {
|
||||
this.selector.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Error closing selector", e);
|
||||
}
|
||||
}
|
||||
if (this.serverChannel == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.serverChannel.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
catch (IOException e) {}
|
||||
this.serverChannel = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -73,6 +73,7 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("UDP Receiver running on port:" + this.getPort());
|
||||
@@ -90,7 +91,7 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
|
||||
// continue
|
||||
}
|
||||
catch (SocketException e) {
|
||||
doStop();
|
||||
this.stop();
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (e instanceof MessagingException) {
|
||||
@@ -133,6 +134,7 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
|
||||
|
||||
protected boolean asyncSendMessage(final DatagramPacket packet) {
|
||||
this.getTaskExecutor().execute(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
Message<byte[]> message = null;
|
||||
try {
|
||||
@@ -222,6 +224,7 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoSendBufferSize(int soSendBufferSize) {
|
||||
this.soSendBufferSize = soSendBufferSize;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2001-2011 the original author or authors.
|
||||
* Copyright 2001-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -51,7 +52,7 @@ import org.springframework.util.Assert;
|
||||
* @since 2.0
|
||||
*/
|
||||
public class UnicastSendingMessageHandler extends
|
||||
AbstractInternetProtocolSendingMessageHandler implements Runnable{
|
||||
AbstractInternetProtocolSendingMessageHandler implements Runnable {
|
||||
|
||||
private final DatagramPacketMessageMapper mapper = new DatagramPacketMessageMapper();
|
||||
|
||||
@@ -86,6 +87,8 @@ public class UnicastSendingMessageHandler extends
|
||||
|
||||
private volatile Executor taskExecutor;
|
||||
|
||||
private volatile boolean taskExecutorSet;
|
||||
|
||||
/**
|
||||
* Basic constructor; no reliability; no acknowledgment.
|
||||
* @param host Destination host.
|
||||
@@ -167,12 +170,14 @@ public class UnicastSendingMessageHandler extends
|
||||
}
|
||||
}
|
||||
|
||||
public void onInit() {
|
||||
@Override
|
||||
public void doStart() {
|
||||
if (this.acknowledge) {
|
||||
if (this.taskExecutor == null) {
|
||||
Executor executor = Executors
|
||||
.newSingleThreadExecutor(new ThreadFactory() {
|
||||
private AtomicInteger n = new AtomicInteger();
|
||||
private final AtomicInteger n = new AtomicInteger();
|
||||
@Override
|
||||
public Thread newThread(Runnable runner) {
|
||||
Thread thread = new Thread(runner);
|
||||
thread.setName("UDP-Ack-Handler-" + n.getAndIncrement());
|
||||
@@ -185,10 +190,21 @@ public class UnicastSendingMessageHandler extends
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() {
|
||||
this.closeSocketIfNeeded();
|
||||
if (!this.taskExecutorSet && this.taskExecutor != null) {
|
||||
((ExecutorService) this.taskExecutor).shutdown();
|
||||
this.taskExecutor = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessageInternal(Message<?> message)
|
||||
throws MessageRejectedException, MessageHandlingException,
|
||||
MessageDeliveryException {
|
||||
if (this.acknowledge) {
|
||||
Assert.state(this.isRunning(), "When 'acknowlege' is enabled, adapter must be running");
|
||||
if (!this.ackThreadRunning) {
|
||||
synchronized(this) {
|
||||
if (!this.ackThreadRunning) {
|
||||
@@ -294,6 +310,7 @@ public class UnicastSendingMessageHandler extends
|
||||
/**
|
||||
* Process acknowledgments, if requested.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
this.ackThreadRunning = true;
|
||||
@@ -333,7 +350,15 @@ public class UnicastSendingMessageHandler extends
|
||||
this.taskExecutor.execute(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use stop() instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void shutDown() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
private void closeSocketIfNeeded() {
|
||||
if (socket != null) {
|
||||
socket.close();
|
||||
socket = null;
|
||||
@@ -344,16 +369,20 @@ public class UnicastSendingMessageHandler extends
|
||||
* @see java.net.Socket#setReceiveBufferSize(int)
|
||||
* @see DatagramSocket#setReceiveBufferSize(int)
|
||||
*/
|
||||
@Override
|
||||
public void setSoReceiveBufferSize(int size) {
|
||||
this.soReceiveBufferSize = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocalAddress(String localAddress) {
|
||||
this.localAddress = localAddress;
|
||||
}
|
||||
|
||||
public void setTaskExecutor(Executor taskExecutor) {
|
||||
Assert.notNull(taskExecutor, "'taskExecutor' cannot be null");
|
||||
this.taskExecutor = taskExecutor;
|
||||
this.taskExecutorSet = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -363,6 +392,7 @@ public class UnicastSendingMessageHandler extends
|
||||
this.ackCounter = ackCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType(){
|
||||
return "ip:udp-outbound-channel-adapter";
|
||||
}
|
||||
|
||||
@@ -677,7 +677,8 @@ public class ParserUnitTests {
|
||||
|
||||
TcpConnectionSupport connection = mock(TcpConnectionSupport.class);
|
||||
TcpConnectionEvent event = new TcpConnectionOpenEvent(connection, "foo");
|
||||
this.eventAdapter.setEventTypes(new Class[] {TcpConnectionEvent.class});
|
||||
Class<TcpConnectionEvent>[] types = (Class<TcpConnectionEvent>[]) new Class<?>[]{TcpConnectionEvent.class};
|
||||
this.eventAdapter.setEventTypes(types);
|
||||
this.eventAdapter.onApplicationEvent(event);
|
||||
assertNull(this.eventChannel.receive(0));
|
||||
this.eventAdapter.start();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -85,10 +85,13 @@ public class TcpOutboundGatewayTests {
|
||||
AbstractConnectionFactory ccf = new TcpNetClientConnectionFactory("localhost", port);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicBoolean done = new AtomicBoolean();
|
||||
final AtomicReference<ServerSocket> serverSocket = new AtomicReference<ServerSocket>();
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port, 100);
|
||||
serverSocket.set(server);
|
||||
latch.countDown();
|
||||
List<Socket> sockets = new ArrayList<Socket>();
|
||||
int i = 0;
|
||||
@@ -100,7 +103,8 @@ public class TcpOutboundGatewayTests {
|
||||
oos.writeObject("Reply" + (i++));
|
||||
sockets.add(socket);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (!done.get()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -140,6 +144,8 @@ public class TcpOutboundGatewayTests {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assertTrue(replies.remove("Reply" + i));
|
||||
}
|
||||
done.set(true);
|
||||
serverSocket.get().close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -148,6 +154,7 @@ public class TcpOutboundGatewayTests {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicBoolean done = new AtomicBoolean();
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port, 10);
|
||||
@@ -201,6 +208,7 @@ public class TcpOutboundGatewayTests {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicBoolean done = new AtomicBoolean();
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
@@ -235,10 +243,11 @@ public class TcpOutboundGatewayTests {
|
||||
gateway.setRequiresReply(true);
|
||||
gateway.setOutputChannel(replyChannel);
|
||||
@SuppressWarnings("unchecked")
|
||||
Future<Integer>[] results = new Future[2];
|
||||
Future<Integer>[] results = (Future<Integer>[]) new Future<?>[2];
|
||||
for (int i = 0; i < 2; i++) {
|
||||
final int j = i;
|
||||
results[j] = (Executors.newSingleThreadExecutor().submit(new Callable<Integer>(){
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
gateway.handleMessage(MessageBuilder.withPayload("Test" + j).build());
|
||||
return 0;
|
||||
@@ -318,6 +327,7 @@ public class TcpOutboundGatewayTests {
|
||||
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
@@ -363,10 +373,11 @@ public class TcpOutboundGatewayTests {
|
||||
gateway.setOutputChannel(replyChannel);
|
||||
gateway.setRemoteTimeout(500);
|
||||
@SuppressWarnings("unchecked")
|
||||
Future<Integer>[] results = new Future[2];
|
||||
Future<Integer>[] results = (Future<Integer>[]) new Future<?>[2];
|
||||
for (int i = 0; i < 2; i++) {
|
||||
final int j = i;
|
||||
results[j] = (Executors.newSingleThreadExecutor().submit(new Callable<Integer>() {
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
// increase the timeout after the first send
|
||||
if (j > 0) {
|
||||
@@ -418,6 +429,7 @@ public class TcpOutboundGatewayTests {
|
||||
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
@@ -497,6 +509,7 @@ public class TcpOutboundGatewayTests {
|
||||
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
@@ -632,14 +645,13 @@ public class TcpOutboundGatewayTests {
|
||||
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
latch.countDown();
|
||||
int i = 0;
|
||||
while (!done.get()) {
|
||||
Socket socket = server.accept();
|
||||
i++;
|
||||
while (!socket.isClosed()) {
|
||||
try {
|
||||
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
@@ -36,13 +37,16 @@ public class ConnectionFactoryShutDownTests {
|
||||
public void testShutdownDoesntDeadlock() throws Exception {
|
||||
final AbstractConnectionFactory factory = new AbstractConnectionFactory(0) {
|
||||
|
||||
@Override
|
||||
public TcpConnection getConnection() throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void close() {
|
||||
}
|
||||
|
||||
};
|
||||
factory.setActive(true);
|
||||
Executor executor = factory.getTaskExecutor();
|
||||
@@ -50,6 +54,7 @@ public class ConnectionFactoryShutDownTests {
|
||||
final CountDownLatch latch2 = new CountDownLatch(1);
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
latch1.countDown();
|
||||
try {
|
||||
|
||||
@@ -65,7 +65,8 @@ public class TcpConnectionEventListenerTests {
|
||||
TcpConnectionEventListeningMessageProducer eventProducer = new TcpConnectionEventListeningMessageProducer();
|
||||
QueueChannel outputChannel = new QueueChannel();
|
||||
eventProducer.setOutputChannel(outputChannel);
|
||||
eventProducer.setEventTypes(new Class[] {FooEvent.class, BarEvent.class});
|
||||
Class<?>[] eventTypes = new Class<?>[]{FooEvent.class, BarEvent.class};
|
||||
eventProducer.setEventTypes((Class<? extends TcpConnectionEvent>[]) eventTypes);
|
||||
eventProducer.afterPropertiesSet();
|
||||
eventProducer.start();
|
||||
TcpConnectionSupport connection = Mockito.mock(TcpConnectionSupport.class);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,9 +20,7 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@@ -32,7 +30,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
import org.springframework.integration.ip.tcp.serializer.AbstractByteArraySerializer;
|
||||
import org.springframework.integration.ip.tcp.serializer.ByteArrayCrLfSerializer;
|
||||
import org.springframework.integration.ip.tcp.serializer.ByteArrayLengthHeaderSerializer;
|
||||
@@ -40,6 +38,7 @@ import org.springframework.integration.ip.tcp.serializer.ByteArrayStxEtxSerializ
|
||||
import org.springframework.integration.ip.util.SocketTestUtils;
|
||||
import org.springframework.integration.ip.util.TestingUtilities;
|
||||
import org.springframework.integration.test.util.SocketUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
@@ -47,7 +46,7 @@ import org.springframework.integration.test.util.SocketUtils;
|
||||
*/
|
||||
public class TcpNioConnectionReadTests {
|
||||
|
||||
private CountDownLatch latch = new CountDownLatch(1);
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
private AbstractServerConnectionFactory getConnectionFactory(int port,
|
||||
AbstractByteArraySerializer serializer, TcpListener listener) throws Exception {
|
||||
@@ -68,10 +67,6 @@ public class TcpNioConnectionReadTests {
|
||||
return scf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.springframework.integration.ip.tcp.NioSocketReader}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testReadLength() throws Exception {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
@@ -79,6 +74,7 @@ public class TcpNioConnectionReadTests {
|
||||
final List<Message<?>> responses = new ArrayList<Message<?>>();
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
AbstractServerConnectionFactory scf = getConnectionFactory(port, serializer,new TcpListener() {
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
responses.add(message);
|
||||
semaphore.release();
|
||||
@@ -88,16 +84,17 @@ public class TcpNioConnectionReadTests {
|
||||
|
||||
// Fire up the sender.
|
||||
|
||||
SocketTestUtils.testSendLength(port, latch);
|
||||
CountDownLatch done = SocketTestUtils.testSendLength(port, latch);
|
||||
latch.countDown();
|
||||
assertTrue(semaphore.tryAcquire(1, 10000, TimeUnit.MILLISECONDS));
|
||||
assertTrue(semaphore.tryAcquire(1, 10000, TimeUnit.MILLISECONDS));
|
||||
assertEquals("Did not receive data", 2, responses.size());
|
||||
assertEquals("Data", SocketTestUtils.TEST_STRING + SocketTestUtils.TEST_STRING,
|
||||
new String(((Message<byte[]>) responses.get(0)).getPayload()));
|
||||
new String((byte[]) responses.get(0).getPayload()));
|
||||
assertEquals("Data", SocketTestUtils.TEST_STRING + SocketTestUtils.TEST_STRING,
|
||||
new String(((Message<byte[]>) responses.get(1)).getPayload()));
|
||||
scf.close();
|
||||
new String((byte[]) responses.get(1).getPayload()));
|
||||
scf.stop();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
|
||||
@@ -110,11 +107,15 @@ public class TcpNioConnectionReadTests {
|
||||
final List<Message<?>> responses = new ArrayList<Message<?>>();
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
AbstractServerConnectionFactory scf = getConnectionFactory(port, serializer,new TcpListener() {
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
responses.add(message);
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) { }
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
semaphore.release();
|
||||
return false;
|
||||
}
|
||||
@@ -123,19 +124,17 @@ public class TcpNioConnectionReadTests {
|
||||
int howMany = 2;
|
||||
scf.setBacklog(howMany + 5);
|
||||
// Fire up the sender.
|
||||
SocketTestUtils.testSendFragmented(port, howMany, false);
|
||||
CountDownLatch done = SocketTestUtils.testSendFragmented(port, howMany, false);
|
||||
assertTrue(semaphore.tryAcquire(howMany, 20000, TimeUnit.MILLISECONDS));
|
||||
assertEquals("Expected", howMany, responses.size());
|
||||
for (int i = 0; i < howMany; i++) {
|
||||
assertEquals("Data", "xx",
|
||||
new String(((Message<byte[]>) responses.get(0)).getPayload()));
|
||||
}
|
||||
scf.close();
|
||||
scf.stop();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.springframework.integration.ip.tcp.NioSocketReader}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testReadStxEtx() throws Exception {
|
||||
@@ -144,6 +143,7 @@ public class TcpNioConnectionReadTests {
|
||||
final List<Message<?>> responses = new ArrayList<Message<?>>();
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
AbstractServerConnectionFactory scf = getConnectionFactory(port, serializer,new TcpListener() {
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
responses.add(message);
|
||||
semaphore.release();
|
||||
@@ -153,7 +153,7 @@ public class TcpNioConnectionReadTests {
|
||||
|
||||
// Fire up the sender.
|
||||
|
||||
SocketTestUtils.testSendStxEtx(port, latch);
|
||||
CountDownLatch done = SocketTestUtils.testSendStxEtx(port, latch);
|
||||
latch.countDown();
|
||||
assertTrue(semaphore.tryAcquire(1, 10000, TimeUnit.MILLISECONDS));
|
||||
assertTrue(semaphore.tryAcquire(1, 10000, TimeUnit.MILLISECONDS));
|
||||
@@ -162,7 +162,8 @@ public class TcpNioConnectionReadTests {
|
||||
new String(((Message<byte[]>) responses.get(0)).getPayload()));
|
||||
assertEquals("Data", SocketTestUtils.TEST_STRING + SocketTestUtils.TEST_STRING,
|
||||
new String(((Message<byte[]>) responses.get(1)).getPayload()));
|
||||
scf.close();
|
||||
scf.stop();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,6 +177,7 @@ public class TcpNioConnectionReadTests {
|
||||
final List<Message<?>> responses = new ArrayList<Message<?>>();
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
AbstractServerConnectionFactory scf = getConnectionFactory(port, serializer,new TcpListener() {
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
responses.add(message);
|
||||
semaphore.release();
|
||||
@@ -185,7 +187,7 @@ public class TcpNioConnectionReadTests {
|
||||
|
||||
// Fire up the sender.
|
||||
|
||||
SocketTestUtils.testSendCrLf(port, latch);
|
||||
CountDownLatch done = SocketTestUtils.testSendCrLf(port, latch);
|
||||
latch.countDown();
|
||||
assertTrue(semaphore.tryAcquire(1, 10000, TimeUnit.MILLISECONDS));
|
||||
assertTrue(semaphore.tryAcquire(1, 10000, TimeUnit.MILLISECONDS));
|
||||
@@ -194,31 +196,30 @@ public class TcpNioConnectionReadTests {
|
||||
new String(((Message<byte[]>) responses.get(0)).getPayload()));
|
||||
assertEquals("Data", SocketTestUtils.TEST_STRING + SocketTestUtils.TEST_STRING,
|
||||
new String(((Message<byte[]>) responses.get(1)).getPayload()));
|
||||
scf.close();
|
||||
scf.stop();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.springframework.integration.ip.tcp.NioSocketReader}.
|
||||
*/
|
||||
@Test
|
||||
public void testReadLengthOverflow() throws Exception {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ByteArrayLengthHeaderSerializer serializer = new ByteArrayLengthHeaderSerializer();
|
||||
final List<Message<?>> responses = new ArrayList<Message<?>>();
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
final List<TcpConnection> added = new ArrayList<TcpConnection>();
|
||||
final List<TcpConnection> removed = new ArrayList<TcpConnection>();
|
||||
AbstractServerConnectionFactory scf = getConnectionFactory(port, serializer,new TcpListener() {
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
responses.add(message);
|
||||
semaphore.release();
|
||||
return false;
|
||||
}
|
||||
}, new TcpSender() {
|
||||
@Override
|
||||
public void addNewConnection(TcpConnection connection) {
|
||||
added.add(connection);
|
||||
semaphore.release();
|
||||
}
|
||||
@Override
|
||||
public void removeDeadConnection(TcpConnection connection) {
|
||||
removed.add(connection);
|
||||
semaphore.release();
|
||||
@@ -227,37 +228,36 @@ public class TcpNioConnectionReadTests {
|
||||
|
||||
// Fire up the sender.
|
||||
|
||||
SocketTestUtils.testSendLengthOverflow(port);
|
||||
CountDownLatch done = SocketTestUtils.testSendLengthOverflow(port);
|
||||
whileOpen(semaphore, added);
|
||||
assertEquals(1, added.size());
|
||||
assertTrue(semaphore.tryAcquire(10000, TimeUnit.MILLISECONDS));
|
||||
assertTrue(removed.size() > 0);
|
||||
scf.close();
|
||||
scf.stop();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.springframework.integration.ip.tcp.NioSocketReader}.
|
||||
*/
|
||||
@Test
|
||||
public void testReadStxEtxOverflow() throws Exception {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ByteArrayStxEtxSerializer serializer = new ByteArrayStxEtxSerializer();
|
||||
serializer.setMaxMessageSize(1024);
|
||||
final List<Message<?>> responses = new ArrayList<Message<?>>();
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
final List<TcpConnection> added = new ArrayList<TcpConnection>();
|
||||
final List<TcpConnection> removed = new ArrayList<TcpConnection>();
|
||||
AbstractServerConnectionFactory scf = getConnectionFactory(port, serializer,new TcpListener() {
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
responses.add(message);
|
||||
semaphore.release();
|
||||
return false;
|
||||
}
|
||||
}, new TcpSender() {
|
||||
@Override
|
||||
public void addNewConnection(TcpConnection connection) {
|
||||
added.add(connection);
|
||||
semaphore.release();
|
||||
}
|
||||
@Override
|
||||
public void removeDeadConnection(TcpConnection connection) {
|
||||
removed.add(connection);
|
||||
semaphore.release();
|
||||
@@ -266,37 +266,36 @@ public class TcpNioConnectionReadTests {
|
||||
|
||||
// Fire up the sender.
|
||||
|
||||
SocketTestUtils.testSendStxEtxOverflow(port);
|
||||
CountDownLatch done = SocketTestUtils.testSendStxEtxOverflow(port);
|
||||
whileOpen(semaphore, added);
|
||||
assertEquals(1, added.size());
|
||||
assertTrue(semaphore.tryAcquire(10000, TimeUnit.MILLISECONDS));
|
||||
assertTrue(removed.size() > 0);
|
||||
scf.close();
|
||||
scf.stop();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.springframework.integration.ip.tcp.NioSocketReader}.
|
||||
*/
|
||||
@Test
|
||||
public void testReadCrLfOverflow() throws Exception {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ByteArrayCrLfSerializer serializer = new ByteArrayCrLfSerializer();
|
||||
serializer.setMaxMessageSize(1024);
|
||||
final List<Message<?>> responses = new ArrayList<Message<?>>();
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
final List<TcpConnection> added = new ArrayList<TcpConnection>();
|
||||
final List<TcpConnection> removed = new ArrayList<TcpConnection>();
|
||||
AbstractServerConnectionFactory scf = getConnectionFactory(port, serializer,new TcpListener() {
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
responses.add(message);
|
||||
semaphore.release();
|
||||
return false;
|
||||
}
|
||||
}, new TcpSender() {
|
||||
@Override
|
||||
public void addNewConnection(TcpConnection connection) {
|
||||
added.add(connection);
|
||||
semaphore.release();
|
||||
}
|
||||
@Override
|
||||
public void removeDeadConnection(TcpConnection connection) {
|
||||
removed.add(connection);
|
||||
semaphore.release();
|
||||
@@ -305,12 +304,13 @@ public class TcpNioConnectionReadTests {
|
||||
|
||||
// Fire up the sender.
|
||||
|
||||
SocketTestUtils.testSendCrLfOverflow(port);
|
||||
CountDownLatch done = SocketTestUtils.testSendCrLfOverflow(port);
|
||||
whileOpen(semaphore, added);
|
||||
assertEquals(1, added.size());
|
||||
assertTrue(semaphore.tryAcquire(10000, TimeUnit.MILLISECONDS));
|
||||
assertTrue(removed.size() > 0);
|
||||
scf.close();
|
||||
scf.stop();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,21 +323,22 @@ public class TcpNioConnectionReadTests {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ByteArrayCrLfSerializer serializer = new ByteArrayCrLfSerializer();
|
||||
serializer.setMaxMessageSize(1024);
|
||||
final List<Message<?>> responses = new ArrayList<Message<?>>();
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
final List<TcpConnection> added = new ArrayList<TcpConnection>();
|
||||
final List<TcpConnection> removed = new ArrayList<TcpConnection>();
|
||||
AbstractServerConnectionFactory scf = getConnectionFactory(port, serializer,new TcpListener() {
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
responses.add(message);
|
||||
semaphore.release();
|
||||
return false;
|
||||
}
|
||||
}, new TcpSender() {
|
||||
@Override
|
||||
public void addNewConnection(TcpConnection connection) {
|
||||
added.add(connection);
|
||||
semaphore.release();
|
||||
}
|
||||
@Override
|
||||
public void removeDeadConnection(TcpConnection connection) {
|
||||
removed.add(connection);
|
||||
semaphore.release();
|
||||
@@ -349,7 +350,7 @@ public class TcpNioConnectionReadTests {
|
||||
assertEquals(1, added.size());
|
||||
assertTrue(semaphore.tryAcquire(10000, TimeUnit.MILLISECONDS));
|
||||
assertTrue(removed.size() > 0);
|
||||
scf.close();
|
||||
scf.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,21 +363,22 @@ public class TcpNioConnectionReadTests {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ByteArrayCrLfSerializer serializer = new ByteArrayCrLfSerializer();
|
||||
serializer.setMaxMessageSize(1024);
|
||||
final List<Message<?>> responses = new ArrayList<Message<?>>();
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
final List<TcpConnection> added = new ArrayList<TcpConnection>();
|
||||
final List<TcpConnection> removed = new ArrayList<TcpConnection>();
|
||||
AbstractServerConnectionFactory scf = getConnectionFactory(port, serializer,new TcpListener() {
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
responses.add(message);
|
||||
semaphore.release();
|
||||
return false;
|
||||
}
|
||||
}, new TcpSender() {
|
||||
@Override
|
||||
public void addNewConnection(TcpConnection connection) {
|
||||
added.add(connection);
|
||||
semaphore.release();
|
||||
}
|
||||
@Override
|
||||
public void removeDeadConnection(TcpConnection connection) {
|
||||
removed.add(connection);
|
||||
semaphore.release();
|
||||
@@ -389,7 +391,7 @@ public class TcpNioConnectionReadTests {
|
||||
assertEquals(1, added.size());
|
||||
assertTrue(semaphore.tryAcquire(10000, TimeUnit.MILLISECONDS));
|
||||
assertTrue(removed.size() > 0);
|
||||
scf.close();
|
||||
scf.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -428,23 +430,25 @@ public class TcpNioConnectionReadTests {
|
||||
}
|
||||
|
||||
private void testClosureMidMessageGuts(AbstractByteArraySerializer serializer, String shortMessage)
|
||||
throws Exception, IOException, UnknownHostException,
|
||||
InterruptedException {
|
||||
throws Exception {
|
||||
final int port = SocketUtils.findAvailableServerSocket();
|
||||
final List<Message<?>> responses = new ArrayList<Message<?>>();
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
final List<TcpConnection> added = new ArrayList<TcpConnection>();
|
||||
final List<TcpConnection> removed = new ArrayList<TcpConnection>();
|
||||
AbstractServerConnectionFactory scf = getConnectionFactory(port, serializer,new TcpListener() {
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
responses.add(message);
|
||||
return false;
|
||||
}
|
||||
}, new TcpSender() {
|
||||
@Override
|
||||
public void addNewConnection(TcpConnection connection) {
|
||||
added.add(connection);
|
||||
semaphore.release();
|
||||
}
|
||||
@Override
|
||||
public void removeDeadConnection(TcpConnection connection) {
|
||||
removed.add(connection);
|
||||
semaphore.release();
|
||||
@@ -457,7 +461,7 @@ public class TcpNioConnectionReadTests {
|
||||
assertEquals(1, added.size());
|
||||
assertTrue(semaphore.tryAcquire(10000, TimeUnit.MILLISECONDS));
|
||||
assertTrue(removed.size() > 0);
|
||||
scf.close();
|
||||
scf.stop();
|
||||
}
|
||||
|
||||
private void whileOpen(Semaphore semaphore, final List<TcpConnection> added)
|
||||
|
||||
@@ -50,6 +50,7 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.net.ServerSocketFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -81,6 +82,7 @@ import org.springframework.util.ReflectionUtils.FieldFilter;
|
||||
public class TcpNioConnectionTests {
|
||||
|
||||
private final ApplicationEventPublisher nullPublisher = new ApplicationEventPublisher() {
|
||||
@Override
|
||||
public void publishEvent(ApplicationEvent event) {
|
||||
}
|
||||
};
|
||||
@@ -92,16 +94,21 @@ public class TcpNioConnectionTests {
|
||||
factory.setSoTimeout(1000);
|
||||
factory.start();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final CountDownLatch done = new CountDownLatch(1);
|
||||
final AtomicReference<ServerSocket> serverSocket = new AtomicReference<ServerSocket>();
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public void run() {
|
||||
try {
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
serverSocket.set(server);
|
||||
latch.countDown();
|
||||
Socket s = server.accept();
|
||||
// block so we fill the buffer
|
||||
server.accept();
|
||||
} catch (Exception e) {
|
||||
done.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -110,10 +117,13 @@ public class TcpNioConnectionTests {
|
||||
try {
|
||||
TcpConnection connection = factory.getConnection();
|
||||
connection.send(MessageBuilder.withPayload(new byte[1000000]).build());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertTrue("Expected SocketTimeoutException, got " + e.getClass().getSimpleName() +
|
||||
":" + e.getMessage(), e instanceof SocketTimeoutException);
|
||||
}
|
||||
done.countDown();
|
||||
serverSocket.get().close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -123,17 +133,22 @@ public class TcpNioConnectionTests {
|
||||
factory.setSoTimeout(1000);
|
||||
factory.start();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final CountDownLatch done = new CountDownLatch(1);
|
||||
final AtomicReference<ServerSocket> serverSocket = new AtomicReference<ServerSocket>();
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
serverSocket.set(server);
|
||||
latch.countDown();
|
||||
Socket socket = server.accept();
|
||||
byte[] b = new byte[6];
|
||||
readFully(socket.getInputStream(), b);
|
||||
// block to cause timeout on read.
|
||||
server.accept();
|
||||
} catch (Exception e) {
|
||||
done.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -150,9 +165,12 @@ public class TcpNioConnectionTests {
|
||||
}
|
||||
}
|
||||
assertTrue(!connection.isOpen());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
fail("Unexpected exception " + e);
|
||||
}
|
||||
done.countDown();
|
||||
serverSocket.get().close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -162,15 +180,19 @@ public class TcpNioConnectionTests {
|
||||
factory.setNioHarvestInterval(100);
|
||||
factory.start();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicReference<ServerSocket> serverSocket = new AtomicReference<ServerSocket>();
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
serverSocket.set(server);
|
||||
latch.countDown();
|
||||
Socket socket = server.accept();
|
||||
byte[] b = new byte[6];
|
||||
readFully(socket.getInputStream(), b);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -182,8 +204,7 @@ public class TcpNioConnectionTests {
|
||||
assertEquals(1, connections.size());
|
||||
connection.close();
|
||||
assertTrue(!connection.isOpen());
|
||||
// force a wakeup of the selector
|
||||
factory.close();
|
||||
TestUtils.getPropertyValue(factory, "selector", Selector.class).wakeup();
|
||||
int n = 0;
|
||||
while (connections.size() > 0) {
|
||||
Thread.sleep(100);
|
||||
@@ -192,11 +213,13 @@ public class TcpNioConnectionTests {
|
||||
}
|
||||
}
|
||||
assertEquals(0, connections.size());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail("Unexpected exception " + e);
|
||||
}
|
||||
factory.stop();
|
||||
serverSocket.get().close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -216,6 +239,7 @@ public class TcpNioConnectionTests {
|
||||
final List<Field> fields = new ArrayList<Field>();
|
||||
ReflectionUtils.doWithFields(SocketChannel.class, new FieldCallback() {
|
||||
|
||||
@Override
|
||||
public void doWith(Field field) throws IllegalArgumentException,
|
||||
IllegalAccessException {
|
||||
field.setAccessible(true);
|
||||
@@ -223,6 +247,7 @@ public class TcpNioConnectionTests {
|
||||
}
|
||||
}, new FieldFilter() {
|
||||
|
||||
@Override
|
||||
public boolean matches(Field field) {
|
||||
return field.getName().equals("open");
|
||||
}});
|
||||
@@ -265,11 +290,13 @@ public class TcpNioConnectionTests {
|
||||
public void testInsufficientThreads() throws Exception {
|
||||
final ExecutorService exec = Executors.newFixedThreadPool(2);
|
||||
Future<Object> future = exec.submit(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
SocketChannel channel = mock(SocketChannel.class);
|
||||
Socket socket = mock(Socket.class);
|
||||
Mockito.when(channel.socket()).thenReturn(socket);
|
||||
doAnswer(new Answer<Integer>() {
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocation) throws Throwable {
|
||||
ByteBuffer buffer = (ByteBuffer) invocation.getArguments()[0];
|
||||
buffer.position(1);
|
||||
@@ -311,11 +338,13 @@ public class TcpNioConnectionTests {
|
||||
final ExecutorService exec = Executors.newFixedThreadPool(3);
|
||||
final CountDownLatch messageLatch = new CountDownLatch(1);
|
||||
Future<Object> future = exec.submit(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
SocketChannel channel = mock(SocketChannel.class);
|
||||
Socket socket = mock(Socket.class);
|
||||
Mockito.when(channel.socket()).thenReturn(socket);
|
||||
doAnswer(new Answer<Integer>() {
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocation) throws Throwable {
|
||||
ByteBuffer buffer = (ByteBuffer) invocation.getArguments()[0];
|
||||
buffer.position(1025);
|
||||
@@ -327,6 +356,7 @@ public class TcpNioConnectionTests {
|
||||
final TcpNioConnection connection = new TcpNioConnection(channel, false, false, null, null);
|
||||
connection.setTaskExecutor(exec);
|
||||
connection.registerListener(new TcpListener(){
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
messageLatch.countDown();
|
||||
return false;
|
||||
@@ -438,6 +468,7 @@ public class TcpNioConnectionTests {
|
||||
final byte[] out = new byte[4];
|
||||
ExecutorService exec = Executors.newSingleThreadExecutor();
|
||||
exec.execute(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
stream.read(out);
|
||||
@@ -468,6 +499,7 @@ public class TcpNioConnectionTests {
|
||||
inboundConnection.setMapper(inMapper);
|
||||
final ByteArrayOutputStream written = new ByteArrayOutputStream();
|
||||
doAnswer(new Answer<Integer>() {
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocation) throws Throwable {
|
||||
ByteBuffer buff = (ByteBuffer) invocation.getArguments()[0];
|
||||
byte[] bytes = written.toByteArray();
|
||||
@@ -481,6 +513,7 @@ public class TcpNioConnectionTests {
|
||||
when(outChannel.socket()).thenReturn(outSocket);
|
||||
TcpNioConnection outboundConnection = new TcpNioConnection(outChannel, true, false, nullPublisher, null);
|
||||
doAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
ByteBuffer buff = (ByteBuffer) invocation.getArguments()[0];
|
||||
byte[] bytes = new byte[buff.limit()];
|
||||
@@ -505,6 +538,7 @@ public class TcpNioConnectionTests {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
TcpListener listener = new TcpListener() {
|
||||
|
||||
@Override
|
||||
public boolean onMessage(Message<?> message) {
|
||||
inboundMessage.set(message);
|
||||
latch.countDown();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,10 +23,13 @@ import java.io.InputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ServerSocketFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.integration.ip.tcp.serializer.AbstractByteArraySerializer;
|
||||
import org.springframework.integration.ip.tcp.serializer.ByteArrayCrLfSerializer;
|
||||
import org.springframework.integration.ip.tcp.serializer.ByteArrayLengthHeaderSerializer;
|
||||
@@ -58,15 +61,18 @@ public class TcpNioConnectionWriteTests {
|
||||
ServerSocket server = ServerSocketFactory.getDefault()
|
||||
.createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ByteArrayLengthHeaderSerializer serializer = new ByteArrayLengthHeaderSerializer();
|
||||
AbstractConnectionFactory ccf = getClientConnectionFactory(false, port, serializer);
|
||||
TcpConnection connection = ccf.getConnection();
|
||||
connection.send(MessageBuilder.withPayload(testString.getBytes()).build());
|
||||
Thread.sleep(1000000000L);
|
||||
} catch (Exception e) {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -82,6 +88,7 @@ public class TcpNioConnectionWriteTests {
|
||||
assertEquals(testString.length(), buffer.getInt());
|
||||
assertEquals(testString, new String(buff, 4, testString.length()));
|
||||
server.close();
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,15 +98,18 @@ public class TcpNioConnectionWriteTests {
|
||||
ServerSocket server = ServerSocketFactory.getDefault()
|
||||
.createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ByteArrayStxEtxSerializer serializer = new ByteArrayStxEtxSerializer();
|
||||
AbstractConnectionFactory ccf = getClientConnectionFactory(false, port, serializer);
|
||||
TcpConnection connection = ccf.getConnection();
|
||||
connection.send(MessageBuilder.withPayload(testString.getBytes()).build());
|
||||
Thread.sleep(1000000000L);
|
||||
} catch (Exception e) {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -115,6 +125,7 @@ public class TcpNioConnectionWriteTests {
|
||||
assertEquals(testString, new String(buff, 1, testString.length()));
|
||||
assertEquals(ByteArrayStxEtxSerializer.ETX, buff[testString.length() + 1]);
|
||||
server.close();
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,15 +135,18 @@ public class TcpNioConnectionWriteTests {
|
||||
ServerSocket server = ServerSocketFactory.getDefault()
|
||||
.createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ByteArrayCrLfSerializer serializer = new ByteArrayCrLfSerializer();
|
||||
AbstractConnectionFactory ccf = getClientConnectionFactory(false, port, serializer);
|
||||
TcpConnection connection = ccf.getConnection();
|
||||
connection.send(MessageBuilder.withPayload(testString.getBytes()).build());
|
||||
Thread.sleep(1000000000L);
|
||||
} catch (Exception e) {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -148,6 +162,7 @@ public class TcpNioConnectionWriteTests {
|
||||
assertEquals('\r', buff[testString.length()]);
|
||||
assertEquals('\n', buff[testString.length() + 1]);
|
||||
server.close();
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -157,15 +172,18 @@ public class TcpNioConnectionWriteTests {
|
||||
ServerSocket server = ServerSocketFactory.getDefault()
|
||||
.createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ByteArrayLengthHeaderSerializer serializer = new ByteArrayLengthHeaderSerializer();
|
||||
AbstractConnectionFactory ccf = getClientConnectionFactory(true, port, serializer);
|
||||
TcpConnection connection = ccf.getConnection();
|
||||
connection.send(MessageBuilder.withPayload(testString.getBytes()).build());
|
||||
Thread.sleep(1000000000L);
|
||||
} catch (Exception e) {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -181,6 +199,7 @@ public class TcpNioConnectionWriteTests {
|
||||
assertEquals(testString.length(), buffer.getInt());
|
||||
assertEquals(testString, new String(buff, 4, testString.length()));
|
||||
server.close();
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -190,16 +209,18 @@ public class TcpNioConnectionWriteTests {
|
||||
ServerSocket server = ServerSocketFactory.getDefault()
|
||||
.createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ByteArrayStxEtxSerializer serializer = new ByteArrayStxEtxSerializer();
|
||||
AbstractConnectionFactory ccf = getClientConnectionFactory(true, port, serializer);
|
||||
TcpConnection connection = ccf.getConnection();
|
||||
connection.send(MessageBuilder.withPayload(testString.getBytes()).build());
|
||||
Thread.sleep(1000000000L);
|
||||
Thread.sleep(1000000000L);
|
||||
} catch (Exception e) {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -215,6 +236,7 @@ public class TcpNioConnectionWriteTests {
|
||||
assertEquals(testString, new String(buff, 1, testString.length()));
|
||||
assertEquals(ByteArrayStxEtxSerializer.ETX, buff[testString.length() + 1]);
|
||||
server.close();
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -224,16 +246,18 @@ public class TcpNioConnectionWriteTests {
|
||||
ServerSocket server = ServerSocketFactory.getDefault()
|
||||
.createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ByteArrayCrLfSerializer serializer = new ByteArrayCrLfSerializer();
|
||||
AbstractConnectionFactory ccf = getClientConnectionFactory(true, port, serializer);
|
||||
TcpConnection connection = ccf.getConnection();
|
||||
connection.send(MessageBuilder.withPayload(testString.getBytes()).build());
|
||||
Thread.sleep(1000000000L);
|
||||
Thread.sleep(1000000000L);
|
||||
} catch (Exception e) {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -249,6 +273,7 @@ public class TcpNioConnectionWriteTests {
|
||||
assertEquals('\r', buff[testString.length()]);
|
||||
assertEquals('\n', buff[testString.length() + 1]);
|
||||
server.close();
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,10 +22,12 @@ import static org.junit.Assert.fail;
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import javax.net.ServerSocketFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.serializer.DefaultDeserializer;
|
||||
import org.springframework.integration.ip.util.SocketTestUtils;
|
||||
import org.springframework.integration.test.util.SocketUtils;
|
||||
@@ -41,7 +43,7 @@ public class DeserializationTests {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
SocketTestUtils.testSendLength(port, null);
|
||||
CountDownLatch done = SocketTestUtils.testSendLength(port, null);
|
||||
Socket socket = server.accept();
|
||||
socket.setSoTimeout(5000);
|
||||
ByteArrayLengthHeaderSerializer serializer = new ByteArrayLengthHeaderSerializer();
|
||||
@@ -52,6 +54,7 @@ public class DeserializationTests {
|
||||
assertEquals("Data", SocketTestUtils.TEST_STRING + SocketTestUtils.TEST_STRING,
|
||||
new String(out));
|
||||
server.close();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -59,7 +62,7 @@ public class DeserializationTests {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
SocketTestUtils.testSendStxEtx(port, null);
|
||||
CountDownLatch done = SocketTestUtils.testSendStxEtx(port, null);
|
||||
Socket socket = server.accept();
|
||||
socket.setSoTimeout(5000);
|
||||
ByteArrayStxEtxSerializer serializer = new ByteArrayStxEtxSerializer();
|
||||
@@ -70,6 +73,7 @@ public class DeserializationTests {
|
||||
assertEquals("Data", SocketTestUtils.TEST_STRING + SocketTestUtils.TEST_STRING,
|
||||
new String(out));
|
||||
server.close();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -77,7 +81,7 @@ public class DeserializationTests {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
SocketTestUtils.testSendCrLf(port, null);
|
||||
CountDownLatch done = SocketTestUtils.testSendCrLf(port, null);
|
||||
Socket socket = server.accept();
|
||||
socket.setSoTimeout(5000);
|
||||
ByteArrayCrLfSerializer serializer = new ByteArrayCrLfSerializer();
|
||||
@@ -88,6 +92,7 @@ public class DeserializationTests {
|
||||
assertEquals("Data", SocketTestUtils.TEST_STRING + SocketTestUtils.TEST_STRING,
|
||||
new String(out));
|
||||
server.close();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -110,7 +115,7 @@ public class DeserializationTests {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
SocketTestUtils.testSendSerialized(port);
|
||||
CountDownLatch done = SocketTestUtils.testSendSerialized(port);
|
||||
Socket socket = server.accept();
|
||||
socket.setSoTimeout(5000);
|
||||
DefaultDeserializer deserializer = new DefaultDeserializer();
|
||||
@@ -119,6 +124,7 @@ public class DeserializationTests {
|
||||
out = deserializer.deserialize(socket.getInputStream());
|
||||
assertEquals("Data", SocketTestUtils.TEST_STRING, out);
|
||||
server.close();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -126,7 +132,7 @@ public class DeserializationTests {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
SocketTestUtils.testSendLengthOverflow(port);
|
||||
CountDownLatch done = SocketTestUtils.testSendLengthOverflow(port);
|
||||
Socket socket = server.accept();
|
||||
socket.setSoTimeout(5000);
|
||||
ByteArrayLengthHeaderSerializer serializer = new ByteArrayLengthHeaderSerializer();
|
||||
@@ -140,6 +146,7 @@ public class DeserializationTests {
|
||||
}
|
||||
}
|
||||
server.close();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -147,7 +154,7 @@ public class DeserializationTests {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
SocketTestUtils.testSendStxEtxOverflow(port);
|
||||
CountDownLatch done = SocketTestUtils.testSendStxEtxOverflow(port);
|
||||
Socket socket = server.accept();
|
||||
socket.setSoTimeout(500);
|
||||
ByteArrayStxEtxSerializer serializer = new ByteArrayStxEtxSerializer();
|
||||
@@ -161,6 +168,7 @@ public class DeserializationTests {
|
||||
}
|
||||
}
|
||||
server.close();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -168,7 +176,7 @@ public class DeserializationTests {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
SocketTestUtils.testSendStxEtxOverflow(port);
|
||||
CountDownLatch done = SocketTestUtils.testSendStxEtxOverflow(port);
|
||||
Socket socket = server.accept();
|
||||
socket.setSoTimeout(5000);
|
||||
ByteArrayStxEtxSerializer serializer = new ByteArrayStxEtxSerializer();
|
||||
@@ -183,6 +191,7 @@ public class DeserializationTests {
|
||||
}
|
||||
}
|
||||
server.close();
|
||||
done.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -190,7 +199,7 @@ public class DeserializationTests {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
SocketTestUtils.testSendCrLfOverflow(port);
|
||||
CountDownLatch latch = SocketTestUtils.testSendCrLfOverflow(port);
|
||||
Socket socket = server.accept();
|
||||
socket.setSoTimeout(500);
|
||||
ByteArrayCrLfSerializer serializer = new ByteArrayCrLfSerializer();
|
||||
@@ -204,6 +213,7 @@ public class DeserializationTests {
|
||||
}
|
||||
}
|
||||
server.close();
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -211,7 +221,7 @@ public class DeserializationTests {
|
||||
int port = SocketUtils.findAvailableServerSocket();
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
SocketTestUtils.testSendCrLfOverflow(port);
|
||||
CountDownLatch latch = SocketTestUtils.testSendCrLfOverflow(port);
|
||||
Socket socket = server.accept();
|
||||
socket.setSoTimeout(5000);
|
||||
ByteArrayCrLfSerializer serializer = new ByteArrayCrLfSerializer();
|
||||
@@ -219,13 +229,15 @@ public class DeserializationTests {
|
||||
try {
|
||||
serializer.deserialize(socket.getInputStream());
|
||||
fail("Expected message length exceeded exception");
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
if (!e.getMessage().startsWith("CRLF not found")) {
|
||||
e.printStackTrace();
|
||||
fail("Unexpected IO Error:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
server.close();
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,11 +24,14 @@ import java.io.ObjectInputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ServerSocketFactory;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.serializer.DefaultSerializer;
|
||||
import org.springframework.integration.test.util.SocketUtils;
|
||||
|
||||
@@ -44,7 +47,9 @@ public class SerializationTests {
|
||||
final String testString = "abcdef";
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socket socket = SocketFactory.getDefault().createSocket("localhost", port);
|
||||
@@ -52,8 +57,9 @@ public class SerializationTests {
|
||||
buffer.put(testString.getBytes());
|
||||
ByteArrayLengthHeaderSerializer serializer = new ByteArrayLengthHeaderSerializer();
|
||||
serializer.serialize(buffer.array(), socket.getOutputStream());
|
||||
Thread.sleep(1000000000L);
|
||||
} catch (Exception e) {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -69,6 +75,7 @@ public class SerializationTests {
|
||||
assertEquals(testString.length(), buffer.getInt());
|
||||
assertEquals(testString, new String(buff, 4, testString.length()));
|
||||
server.close();
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -77,7 +84,9 @@ public class SerializationTests {
|
||||
final String testString = "abcdef";
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socket socket = SocketFactory.getDefault().createSocket("localhost", port);
|
||||
@@ -85,8 +94,9 @@ public class SerializationTests {
|
||||
buffer.put(testString.getBytes());
|
||||
ByteArrayStxEtxSerializer serializer = new ByteArrayStxEtxSerializer();
|
||||
serializer.serialize(buffer.array(), socket.getOutputStream());
|
||||
Thread.sleep(1000000000L);
|
||||
} catch (Exception e) {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -102,6 +112,7 @@ public class SerializationTests {
|
||||
assertEquals(testString, new String(buff, 1, testString.length()));
|
||||
assertEquals(ByteArrayStxEtxSerializer.ETX, buff[testString.length() + 1]);
|
||||
server.close();
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -110,7 +121,9 @@ public class SerializationTests {
|
||||
final String testString = "abcdef";
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socket socket = SocketFactory.getDefault().createSocket("localhost", port);
|
||||
@@ -118,8 +131,9 @@ public class SerializationTests {
|
||||
buffer.put(testString.getBytes());
|
||||
ByteArrayCrLfSerializer serializer = new ByteArrayCrLfSerializer();
|
||||
serializer.serialize(buffer.array(), socket.getOutputStream());
|
||||
Thread.sleep(1000000000L);
|
||||
} catch (Exception e) {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -135,6 +149,7 @@ public class SerializationTests {
|
||||
assertEquals('\r', buff[testString.length()]);
|
||||
assertEquals('\n', buff[testString.length() + 1]);
|
||||
server.close();
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -143,7 +158,9 @@ public class SerializationTests {
|
||||
final String testString = "abcdef";
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socket socket = SocketFactory.getDefault().createSocket("localhost", port);
|
||||
@@ -152,8 +169,9 @@ public class SerializationTests {
|
||||
ByteArrayRawSerializer serializer = new ByteArrayRawSerializer();
|
||||
serializer.serialize(buffer.array(), socket.getOutputStream());
|
||||
socket.close();
|
||||
Thread.sleep(1000000000L);
|
||||
} catch (Exception e) {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -167,6 +185,7 @@ public class SerializationTests {
|
||||
readFully(is, buff);
|
||||
assertEquals(testString, new String(buff, 0, testString.length()));
|
||||
assertEquals(-1, buff[testString.length()]);
|
||||
latch.countDown();
|
||||
server.close();
|
||||
}
|
||||
|
||||
@@ -176,15 +195,18 @@ public class SerializationTests {
|
||||
final String testString = "abcdef";
|
||||
ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(port);
|
||||
server.setSoTimeout(10000);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socket socket = SocketFactory.getDefault().createSocket("localhost", port);
|
||||
DefaultSerializer serializer = new DefaultSerializer();
|
||||
serializer.serialize(testString, socket.getOutputStream());
|
||||
serializer.serialize(testString, socket.getOutputStream());
|
||||
Thread.sleep(1000000000L);
|
||||
} catch (Exception e) {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -198,6 +220,7 @@ public class SerializationTests {
|
||||
assertEquals(testString, ois.readObject());
|
||||
ois = new ObjectInputStream(is);
|
||||
assertEquals(testString, ois.readObject());
|
||||
latch.countDown();
|
||||
server.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,10 +33,11 @@ import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
import org.springframework.integration.ip.IpHeaders;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.SocketUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
@@ -54,6 +55,7 @@ public class DatagramPacketSendingHandlerTests {
|
||||
final DatagramPacket receivedPacket = new DatagramPacket(buffer, buffer.length);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
DatagramSocket socket = new DatagramSocket(testPort);
|
||||
@@ -78,7 +80,7 @@ public class DatagramPacketSendingHandlerTests {
|
||||
byte[] dest = new byte[length];
|
||||
System.arraycopy(src, offset, dest, 0, length);
|
||||
assertEquals(payload, new String(dest));
|
||||
handler.shutDown();
|
||||
handler.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,7 +99,9 @@ public class DatagramPacketSendingHandlerTests {
|
||||
new UnicastSendingMessageHandler("localhost", testPort, true,
|
||||
true, "localhost", ackPort, 5000);
|
||||
handler.afterPropertiesSet();
|
||||
handler.start();
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
DatagramSocket socket = new DatagramSocket(testPort);
|
||||
@@ -132,7 +136,7 @@ public class DatagramPacketSendingHandlerTests {
|
||||
byte[] dest = new byte[6];
|
||||
System.arraycopy(src, offset+length-6, dest, 0, 6);
|
||||
assertEquals(payload, new String(dest));
|
||||
handler.shutDown();
|
||||
handler.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -144,6 +148,7 @@ public class DatagramPacketSendingHandlerTests {
|
||||
final CountDownLatch latch1 = new CountDownLatch(2);
|
||||
final CountDownLatch latch2 = new CountDownLatch(2);
|
||||
Runnable catcher = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
byte[] buffer = new byte[8];
|
||||
@@ -183,7 +188,7 @@ public class DatagramPacketSendingHandlerTests {
|
||||
MulticastSendingMessageHandler handler = new MulticastSendingMessageHandler(multicastAddress, testPort);
|
||||
handler.handleMessage(MessageBuilder.withPayload(payload).build());
|
||||
assertTrue(latch2.await(3000, TimeUnit.MILLISECONDS));
|
||||
handler.shutDown();
|
||||
handler.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -200,6 +205,7 @@ public class DatagramPacketSendingHandlerTests {
|
||||
final CountDownLatch latch1 = new CountDownLatch(2);
|
||||
final CountDownLatch latch2 = new CountDownLatch(2);
|
||||
Runnable catcher = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
byte[] buffer = new byte[1000];
|
||||
@@ -249,9 +255,11 @@ public class DatagramPacketSendingHandlerTests {
|
||||
new MulticastSendingMessageHandler(multicastAddress, testPort, true,
|
||||
true, "localhost", ackPort, 500000);
|
||||
handler.setMinAcksForSuccess(2);
|
||||
handler.afterPropertiesSet();
|
||||
handler.start();
|
||||
handler.handleMessage(MessageBuilder.withPayload(payload).build());
|
||||
assertTrue(latch2.await(10000, TimeUnit.MILLISECONDS));
|
||||
handler.shutDown();
|
||||
handler.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,15 +16,18 @@
|
||||
package org.springframework.integration.ip.udp;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.ip.util.SocketTestUtils;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.SocketUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
|
||||
/**
|
||||
@@ -45,7 +48,8 @@ import org.springframework.integration.test.util.SocketUtils;
|
||||
public class MultiClientTests {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test @Ignore
|
||||
@Test
|
||||
@Ignore
|
||||
public void testNoAck() throws Exception {
|
||||
final String payload = largePayload(1000);
|
||||
final UnicastReceivingChannelAdapter adapter =
|
||||
@@ -57,15 +61,24 @@ public class MultiClientTests {
|
||||
adapter.start();
|
||||
final QueueChannel queueIn = new QueueChannel(1000);
|
||||
SocketTestUtils.waitListening(adapter);
|
||||
|
||||
final AtomicBoolean done = new AtomicBoolean();
|
||||
|
||||
for (int i = 0; i < drivers; i++) {
|
||||
Thread t = new Thread( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
UnicastSendingMessageHandler sender = new UnicastSendingMessageHandler(
|
||||
"localhost", adapter.getPort());
|
||||
sender.start();
|
||||
while (true) {
|
||||
Message<?> message = queueIn.receive();
|
||||
sender.handleMessage(message);
|
||||
if (done.get()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
sender.stop();
|
||||
}});
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
@@ -79,12 +92,13 @@ public class MultiClientTests {
|
||||
Assert.assertEquals(payload, new String(messageOut.getPayload()));
|
||||
}
|
||||
adapter.stop();
|
||||
done.set(true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test @Ignore
|
||||
@Test
|
||||
@Ignore
|
||||
public void testAck() throws Exception {
|
||||
Thread.sleep(1000);
|
||||
final String payload = largePayload(1000);
|
||||
final UnicastReceivingChannelAdapter adapter =
|
||||
new UnicastReceivingChannelAdapter(SocketUtils.findAvailableUdpSocket(), false);
|
||||
@@ -95,19 +109,28 @@ public class MultiClientTests {
|
||||
adapter.start();
|
||||
final QueueChannel queueIn = new QueueChannel(1000);
|
||||
SocketTestUtils.waitListening(adapter);
|
||||
|
||||
final AtomicBoolean done = new AtomicBoolean();
|
||||
|
||||
for (int i = 0; i < drivers; i++) {
|
||||
final int j = i;
|
||||
Thread t = new Thread( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
UnicastSendingMessageHandler sender = new UnicastSendingMessageHandler(
|
||||
"localhost", adapter.getPort(),
|
||||
false, true, "localhost",
|
||||
SocketUtils.findAvailableUdpSocket(adapter.getPort() + j + 1000),
|
||||
10000);
|
||||
sender.start();
|
||||
while (true) {
|
||||
Message<?> message = queueIn.receive();
|
||||
sender.handleMessage(message);
|
||||
if (done.get()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
sender.stop();
|
||||
}});
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
@@ -121,12 +144,13 @@ public class MultiClientTests {
|
||||
Assert.assertEquals(payload, new String(messageOut.getPayload()));
|
||||
}
|
||||
adapter.stop();
|
||||
done.set(true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test @Ignore
|
||||
@Test
|
||||
@Ignore
|
||||
public void testAckWithLength() throws Exception {
|
||||
Thread.sleep(1000);
|
||||
final String payload = largePayload(1000);
|
||||
final UnicastReceivingChannelAdapter adapter =
|
||||
new UnicastReceivingChannelAdapter(SocketUtils.findAvailableUdpSocket(), true);
|
||||
@@ -137,19 +161,28 @@ public class MultiClientTests {
|
||||
adapter.start();
|
||||
final QueueChannel queueIn = new QueueChannel(1000);
|
||||
SocketTestUtils.waitListening(adapter);
|
||||
|
||||
final AtomicBoolean done = new AtomicBoolean();
|
||||
|
||||
for (int i = 0; i < drivers; i++) {
|
||||
final int j = i;
|
||||
Thread t = new Thread( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
UnicastSendingMessageHandler sender = new UnicastSendingMessageHandler(
|
||||
"localhost", adapter.getPort(),
|
||||
true, true, "localhost",
|
||||
SocketUtils.findAvailableUdpSocket(adapter.getPort() + j + 1100),
|
||||
10000);
|
||||
sender.start();
|
||||
while (true) {
|
||||
Message<?> message = queueIn.receive();
|
||||
sender.handleMessage(message);
|
||||
if (done.get()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
sender.stop();
|
||||
}});
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
@@ -163,6 +196,7 @@ public class MultiClientTests {
|
||||
Assert.assertEquals(payload, new String(messageOut.getPayload()));
|
||||
}
|
||||
adapter.stop();
|
||||
done.set(true);
|
||||
}
|
||||
|
||||
private String largePayload(int n) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,6 +29,6 @@ public class SyslogdTests {
|
||||
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("SyslogdTests-context.xml", SyslogdTests.class);
|
||||
System.out.println("Hit enter to terminate");
|
||||
System.in.read();
|
||||
ctx.destroy();
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,15 +32,16 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
import org.springframework.integration.handler.ServiceActivatingHandler;
|
||||
import org.springframework.integration.ip.IpHeaders;
|
||||
import org.springframework.integration.ip.util.SocketTestUtils;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.SocketUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -65,9 +66,12 @@ public class UdpChannelAdapterTests {
|
||||
DatagramPacketMessageMapper mapper = new DatagramPacketMessageMapper();
|
||||
DatagramPacket packet = mapper.fromMessage(message);
|
||||
packet.setSocketAddress(new InetSocketAddress("localhost", port));
|
||||
new DatagramSocket(SocketUtils.findAvailableUdpSocket()).send(packet);
|
||||
DatagramSocket datagramSocket = new DatagramSocket(SocketUtils.findAvailableUdpSocket());
|
||||
datagramSocket.send(packet);
|
||||
datagramSocket.close();
|
||||
Message<byte[]> receivedMessage = (Message<byte[]>) channel.receive(2000);
|
||||
assertEquals(new String(message.getPayload()), new String(receivedMessage.getPayload()));
|
||||
adapter.stop();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -91,6 +95,7 @@ public class UdpChannelAdapterTests {
|
||||
final CountDownLatch replyReceivedLatch = new CountDownLatch(1);
|
||||
//main thread sends the reply using the headers, this thread will receive it
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
DatagramPacket answer = new DatagramPacket(new byte[2000], 2000);
|
||||
try {
|
||||
@@ -112,11 +117,15 @@ public class UdpChannelAdapterTests {
|
||||
(String) receivedMessage.getHeaders().get(IpHeaders.IP_ADDRESS),
|
||||
(Integer) receivedMessage.getHeaders().get(IpHeaders.PORT)));
|
||||
assertTrue(receiverReadyLatch.await(10, TimeUnit.SECONDS));
|
||||
new DatagramSocket().send(reply);
|
||||
DatagramSocket datagramSocket = new DatagramSocket();
|
||||
datagramSocket.send(reply);
|
||||
assertTrue(replyReceivedLatch.await(10, TimeUnit.SECONDS));
|
||||
DatagramPacket answerPacket = theAnswer.get();
|
||||
assertNotNull(answerPacket);
|
||||
assertEquals(replyString, new String(answerPacket.getData(), 0, answerPacket.getLength()));
|
||||
datagramSocket.close();
|
||||
socket.close();
|
||||
adapter.stop();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -139,10 +148,13 @@ public class UdpChannelAdapterTests {
|
||||
SocketUtils.findAvailableUdpSocket(), 5000);
|
||||
// handler.setLocalAddress(whichNic);
|
||||
handler.afterPropertiesSet();
|
||||
handler.start();
|
||||
Message<byte[]> message = MessageBuilder.withPayload("ABCD".getBytes()).build();
|
||||
handler.handleMessage(message);
|
||||
Message<byte[]> receivedMessage = (Message<byte[]>) channel.receive(2000);
|
||||
assertEquals(new String(message.getPayload()), new String(receivedMessage.getPayload()));
|
||||
adapter.stop();
|
||||
handler.stop();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -165,11 +177,14 @@ public class UdpChannelAdapterTests {
|
||||
DatagramPacketMessageMapper mapper = new DatagramPacketMessageMapper();
|
||||
DatagramPacket packet = mapper.fromMessage(message);
|
||||
packet.setSocketAddress(new InetSocketAddress("225.6.7.8", port));
|
||||
new DatagramSocket(0, Inet4Address.getByName(nic)).send(packet);
|
||||
DatagramSocket datagramSocket = new DatagramSocket(0, Inet4Address.getByName(nic));
|
||||
datagramSocket.send(packet);
|
||||
datagramSocket.close();
|
||||
|
||||
Message<byte[]> receivedMessage = (Message<byte[]>) channel.receive(2000);
|
||||
assertNotNull(receivedMessage);
|
||||
assertEquals(new String(message.getPayload()), new String(receivedMessage.getPayload()));
|
||||
adapter.stop();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -196,6 +211,7 @@ public class UdpChannelAdapterTests {
|
||||
Message<byte[]> receivedMessage = (Message<byte[]>) channel.receive(2000);
|
||||
assertNotNull(receivedMessage);
|
||||
assertEquals(new String(message.getPayload()), new String(receivedMessage.getPayload()));
|
||||
adapter.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -217,10 +233,13 @@ public class UdpChannelAdapterTests {
|
||||
DatagramPacketMessageMapper mapper = new DatagramPacketMessageMapper();
|
||||
DatagramPacket packet = mapper.fromMessage(message);
|
||||
packet.setSocketAddress(new InetSocketAddress("localhost", port));
|
||||
new DatagramSocket(SocketUtils.findAvailableUdpSocket()).send(packet);
|
||||
DatagramSocket datagramSocket = new DatagramSocket(SocketUtils.findAvailableUdpSocket());
|
||||
datagramSocket.send(packet);
|
||||
datagramSocket.close();
|
||||
Message<?> receivedMessage = errorChannel.receive(2000);
|
||||
assertNotNull(receivedMessage);
|
||||
assertEquals("Failed", ((Exception) receivedMessage.getPayload()).getCause().getMessage());
|
||||
adapter.stop();
|
||||
}
|
||||
|
||||
private class FailingService {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -122,6 +122,7 @@ public class UdpMulticastEndToEndTests implements Runnable {
|
||||
/**
|
||||
* Instantiate the receiving context
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run() {
|
||||
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(
|
||||
@@ -146,6 +147,7 @@ public class UdpMulticastEndToEndTests implements Runnable {
|
||||
}
|
||||
}
|
||||
ctx.stop();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -143,7 +143,9 @@ public class UdpUnicastEndToEndTests implements Runnable {
|
||||
* Instantiate the receiving context
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void run() {
|
||||
@SuppressWarnings("resource")
|
||||
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(
|
||||
"testIp-in-context.xml", UdpUnicastEndToEndTests.class);
|
||||
UnicastReceivingChannelAdapter inbound = ctx.getBean(UnicastReceivingChannelAdapter.class);
|
||||
@@ -155,7 +157,8 @@ public class UdpUnicastEndToEndTests implements Runnable {
|
||||
throw new RuntimeException("Failed to start listening");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) { }
|
||||
}
|
||||
catch (Exception e) { }
|
||||
while (okToRun) {
|
||||
try {
|
||||
readyToReceive.countDown();
|
||||
@@ -179,6 +182,7 @@ public class UdpUnicastEndToEndTests implements Runnable {
|
||||
}
|
||||
}
|
||||
ctx.stop();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
http://www.springframework.org/schema/integration/ip/spring-integration-ip.xsd">
|
||||
|
||||
<stream:stdin-channel-adapter id="stdin" channel="mcOutputChannel" >
|
||||
<poller>
|
||||
<interval-trigger interval="100" time-unit="MILLISECONDS"/>
|
||||
</poller>
|
||||
<poller fixed-delay="100" />
|
||||
</stream:stdin-channel-adapter>
|
||||
|
||||
<channel id="mcInputChannel"/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.integration.ip.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
@@ -23,6 +24,7 @@ import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -46,11 +48,14 @@ public class SocketTestUtils {
|
||||
* Sends a message in two chunks with a preceding length. Two such messages are sent.
|
||||
* @param latch If not null, await until counted down before sending second chunk.
|
||||
*/
|
||||
public static void testSendLength(final int port, final CountDownLatch latch) {
|
||||
public static CountDownLatch testSendLength(final int port, final CountDownLatch latch) {
|
||||
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Socket socket = null;
|
||||
try {
|
||||
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
byte[] len = new byte[4];
|
||||
ByteBuffer.wrap(len).putInt(TEST_STRING.length() * 2);
|
||||
@@ -65,48 +70,74 @@ public class SocketTestUtils {
|
||||
socket.getOutputStream().write(TEST_STRING.getBytes());
|
||||
logger.debug(i + " Wrote second part");
|
||||
}
|
||||
Thread.sleep(1000000000L); // wait forever, but we're a daemon
|
||||
} catch (Exception e) {
|
||||
testCompleteLatch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
return testCompleteLatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message with a bad length part, causing an overflow on the receiver.
|
||||
*/
|
||||
public static void testSendLengthOverflow(final int port) {
|
||||
public static CountDownLatch testSendLengthOverflow(final int port) {
|
||||
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Socket socket = null;
|
||||
try {
|
||||
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
byte[] len = new byte[4];
|
||||
ByteBuffer.wrap(len).putInt(Integer.MAX_VALUE);
|
||||
socket.getOutputStream().write(len);
|
||||
socket.getOutputStream().write(TEST_STRING.getBytes());
|
||||
Thread.sleep(1000000000L); // wait forever, but we're a daemon
|
||||
} catch (Exception e) {
|
||||
testCompleteLatch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
return testCompleteLatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for reassembly of completely fragmented message; sends
|
||||
* 6 bytes 500ms apart.
|
||||
*/
|
||||
public static void testSendFragmented(final int port, final int howMany, final boolean noDelay) {
|
||||
public static CountDownLatch testSendFragmented(final int port, final int howMany, final boolean noDelay) {
|
||||
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Socket socket = null;
|
||||
try {
|
||||
logger.debug("Connecting to " + port);
|
||||
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
OutputStream os = socket.getOutputStream();
|
||||
for (int i = 0; i < howMany; i++) {
|
||||
writeByte(os, 0, noDelay);
|
||||
@@ -116,14 +147,24 @@ public class SocketTestUtils {
|
||||
writeByte(os, 'x', noDelay);
|
||||
writeByte(os, 'x', noDelay);
|
||||
}
|
||||
Thread.sleep(1000000000L); // wait forever, but we're a daemon
|
||||
} catch (Exception e) {
|
||||
testCompleteLatch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
return testCompleteLatch;
|
||||
}
|
||||
|
||||
private static void writeByte(OutputStream os, int b, boolean noDelay) throws Exception {
|
||||
@@ -139,11 +180,14 @@ public class SocketTestUtils {
|
||||
* Sends a STX/ETX message in two chunks. Two such messages are sent.
|
||||
* @param latch If not null, await until counted down before sending second chunk.
|
||||
*/
|
||||
public static void testSendStxEtx(final int port, final CountDownLatch latch) {
|
||||
public static CountDownLatch testSendStxEtx(final int port, final CountDownLatch latch) {
|
||||
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Socket socket = null;
|
||||
try {
|
||||
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
OutputStream outputStream = socket.getOutputStream();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
writeByte(outputStream, 0x02, true);
|
||||
@@ -158,46 +202,74 @@ public class SocketTestUtils {
|
||||
logger.debug(i + " Wrote second part");
|
||||
writeByte(outputStream, 0x03, true);
|
||||
}
|
||||
Thread.sleep(1000000000L); // wait forever, but we're a daemon
|
||||
} catch (Exception e) {
|
||||
testCompleteLatch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
return testCompleteLatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a large STX/ETX message with no ETX
|
||||
*/
|
||||
public static void testSendStxEtxOverflow(final int port) {
|
||||
public static CountDownLatch testSendStxEtxOverflow(final int port) {
|
||||
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Socket socket = null;
|
||||
try {
|
||||
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
OutputStream outputStream = socket.getOutputStream();
|
||||
writeByte(outputStream, 0x02, true);
|
||||
for (int i = 0; i < 1500; i++) {
|
||||
writeByte(outputStream, 'x', true);
|
||||
}
|
||||
Thread.sleep(1000000000L); // wait forever, but we're a daemon
|
||||
} catch (Exception e) { }
|
||||
testCompleteLatch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
return testCompleteLatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message +CRLF in two chunks. Two such messages are sent.
|
||||
* @param latch If not null, await until counted down before sending second chunk.
|
||||
*/
|
||||
public static void testSendCrLf(final int port, final CountDownLatch latch) {
|
||||
public static CountDownLatch testSendCrLf(final int port, final CountDownLatch latch) {
|
||||
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Socket socket = null;
|
||||
try {
|
||||
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
OutputStream outputStream = socket.getOutputStream();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
outputStream.write(TEST_STRING.getBytes());
|
||||
@@ -212,14 +284,24 @@ public class SocketTestUtils {
|
||||
writeByte(outputStream, '\r', true);
|
||||
writeByte(outputStream, '\n', true);
|
||||
}
|
||||
Thread.sleep(1000000000L); // wait forever, but we're a daemon
|
||||
} catch (Exception e) {
|
||||
testCompleteLatch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
return testCompleteLatch;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,6 +310,7 @@ public class SocketTestUtils {
|
||||
*/
|
||||
public static void testSendCrLfSingle(final int port, final CountDownLatch latch) {
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
@@ -240,7 +323,8 @@ public class SocketTestUtils {
|
||||
latch.await();
|
||||
}
|
||||
socket.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -254,6 +338,7 @@ public class SocketTestUtils {
|
||||
*/
|
||||
public static void testSendRaw(final int port) {
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
@@ -261,7 +346,8 @@ public class SocketTestUtils {
|
||||
outputStream.write(TEST_STRING.getBytes());
|
||||
outputStream.write(TEST_STRING.getBytes());
|
||||
socket.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -273,11 +359,14 @@ public class SocketTestUtils {
|
||||
* Sends two serialized objects over the same socket.
|
||||
* @param port
|
||||
*/
|
||||
public static void testSendSerialized(final int port) {
|
||||
public static CountDownLatch testSendSerialized(final int port) {
|
||||
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Socket socket = null;
|
||||
try {
|
||||
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
OutputStream outputStream = socket.getOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(outputStream);
|
||||
oos.writeObject(TEST_STRING);
|
||||
@@ -285,21 +374,33 @@ public class SocketTestUtils {
|
||||
oos = new ObjectOutputStream(outputStream);
|
||||
oos.writeObject(TEST_STRING);
|
||||
oos.flush();
|
||||
Thread.sleep(1000000000L); // wait forever, but we're a daemon
|
||||
} catch (Exception e) {
|
||||
testCompleteLatch.await(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
return testCompleteLatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a large CRLF message with no CRLF.
|
||||
*/
|
||||
public static void testSendCrLfOverflow(final int port) {
|
||||
public static CountDownLatch testSendCrLfOverflow(final int port) {
|
||||
final CountDownLatch testCompleteLatch = new CountDownLatch(1);
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
|
||||
@@ -307,12 +408,15 @@ public class SocketTestUtils {
|
||||
for (int i = 0; i < 1500; i++) {
|
||||
writeByte(outputStream, 'x', true);
|
||||
}
|
||||
Thread.sleep(1000000000L); // wait forever, but we're a daemon
|
||||
} catch (Exception e) { }
|
||||
testCompleteLatch.await(10, TimeUnit.SECONDS);
|
||||
socket.close();
|
||||
}
|
||||
catch (Exception e) { }
|
||||
}
|
||||
});
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
return testCompleteLatch;
|
||||
}
|
||||
|
||||
public static void setLocalNicIfPossible(
|
||||
|
||||
@@ -490,19 +490,6 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
this.usePayloadAsParameterSource = usePayloadAsParameterSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether a Stored Procedure or a Function is being executed.
|
||||
* The default value is false.
|
||||
*
|
||||
* @param isFunction If set to true an Sql Function is executed rather than a Stored Procedure.
|
||||
*
|
||||
* @deprecated Please use {@link #setIsFunction(boolean)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setFunction(boolean isFunction) {
|
||||
this.isFunction = isFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether a Stored Procedure or a Function is being executed.
|
||||
* The default value is false.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
@@ -13,19 +13,12 @@
|
||||
|
||||
package org.springframework.integration.jdbc;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.integration.handler.AbstractMessageHandler;
|
||||
import org.springframework.integration.jdbc.storedproc.ProcedureParameter;
|
||||
import org.springframework.jdbc.core.SqlParameter;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcCallOperations;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
@@ -55,27 +48,6 @@ public class StoredProcMessageHandler extends AbstractMessageHandler implements
|
||||
|
||||
private final StoredProcExecutor executor;
|
||||
|
||||
/**
|
||||
* Constructor taking {@link DataSource} from which the DB Connection can be
|
||||
* obtained and the name of the stored procedure or function to
|
||||
* execute to retrieve new rows.
|
||||
*
|
||||
* @param dataSource Must not be null.
|
||||
* @param storedProcedureName The name of the Stored Procedure or Function. Must not be null.
|
||||
*
|
||||
* @deprecated Since 2.2 use the constructor that expects a {@link StoredProcExecutor} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public StoredProcMessageHandler(DataSource dataSource, String storedProcedureName) {
|
||||
|
||||
Assert.notNull(dataSource, "dataSource must not be null.");
|
||||
Assert.hasText(storedProcedureName, "storedProcedureName must not be null and cannot be empty.");
|
||||
|
||||
this.executor = new StoredProcExecutor(dataSource);
|
||||
this.executor.setStoredProcedureName(storedProcedureName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Constructor passing in the {@link StoredProcExecutor}.
|
||||
@@ -84,22 +56,11 @@ public class StoredProcMessageHandler extends AbstractMessageHandler implements
|
||||
*
|
||||
*/
|
||||
public StoredProcMessageHandler(StoredProcExecutor storedProcExecutor) {
|
||||
|
||||
Assert.notNull(storedProcExecutor, "storedProcExecutor must not be null.");
|
||||
this.executor = storedProcExecutor;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies parameters, sets the parameters on {@link SimpleJdbcCallOperations}
|
||||
* and ensures the appropriate {@link SqlParameterSourceFactory} is defined
|
||||
* when {@link ProcedureParameter} are passed in.
|
||||
*/
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
super.onInit();
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes the Stored procedure, delegates to executeStoredProcedure(...).
|
||||
* Any return values from the Stored procedure are ignored.
|
||||
@@ -115,121 +76,11 @@ public class StoredProcMessageHandler extends AbstractMessageHandler implements
|
||||
|
||||
if (resultMap != null && !resultMap.isEmpty()) {
|
||||
logger.debug(String.format("The StoredProcMessageHandler ignores return "
|
||||
+ "values, but the called Stored Procedure returned data: '%s'", executor.getStoredProcedureName(), resultMap));
|
||||
+ "values, but the called Stored Procedure '%s' returned data: '%s'", executor.getStoredProcedureName(), resultMap));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the Stored Procedure or Stored Function to be executed.
|
||||
* If {@link StoredProcExecutor#isFunction} is set to "true", then this
|
||||
* property specifies the Stored Function name.
|
||||
*
|
||||
* Alternatively you can also specify the Stored Procedure name via
|
||||
* {@link StoredProcExecutor#setStoredProcedureNameExpression(Expression)}.
|
||||
*
|
||||
* @param storedProcedureName Must not be null and must not be empty
|
||||
*
|
||||
* @see StoredProcExecutor#setStoredProcedureNameExpression(Expression)
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setStoredProcedureName(String)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setStoredProcedureName(String storedProcedureName) {
|
||||
this.executor.setStoredProcedureName(storedProcedureName);
|
||||
}
|
||||
|
||||
/**
|
||||
* For fully supported databases, the underlying {@link SimpleJdbcCall} can
|
||||
* retrieve the parameter information for the to be invoked Stored Procedure
|
||||
* from the JDBC Meta-data. However, if the used database does not support
|
||||
* meta data lookups or if you like to provide customized parameter definitions,
|
||||
* this flag can be set to <code>true</code>. It defaults to <code>false</code>.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setIgnoreColumnMetaData(boolean)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setIgnoreColumnMetaData(boolean ignoreColumnMetaData) {
|
||||
this.executor.setIgnoreColumnMetaData(ignoreColumnMetaData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom Stored Procedure parameters that may contain static values
|
||||
* or Strings representing an {@link Expression}.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setProcedureParameters(List)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setProcedureParameters(List<ProcedureParameter> procedureParameters) {
|
||||
this.executor.setProcedureParameters(procedureParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* If your database system is not fully supported by Spring and thus obtaining
|
||||
* parameter definitions from the JDBC Meta-data is not possible, you must define
|
||||
* the {@link SqlParameter} explicitly.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setSqlParameters(List)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSqlParameters(List<SqlParameter> sqlParameters) {
|
||||
this.executor.setSqlParameters(sqlParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the ability to set a custom {@link SqlParameterSourceFactory}.
|
||||
* Keep in mind that if {@link ProcedureParameter} are set explicitly and
|
||||
* you would like to provide a custom {@link SqlParameterSourceFactory},
|
||||
* then you must provide an instance of {@link ExpressionEvaluatingSqlParameterSourceFactory}.
|
||||
*
|
||||
* If not the SqlParameterSourceFactory will be replaced by the default
|
||||
* {@link ExpressionEvaluatingSqlParameterSourceFactory}.
|
||||
*
|
||||
* @param sqlParameterSourceFactory
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setSqlParameterSourceFactory(SqlParameterSourceFactory)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSqlParameterSourceFactory(SqlParameterSourceFactory sqlParameterSourceFactory) {
|
||||
this.executor.setSqlParameterSourceFactory(sqlParameterSourceFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to 'true', the payload of the Message will be used as a source for
|
||||
* providing parameters. If false the entire Message will be available as a
|
||||
* source for parameters.
|
||||
*
|
||||
* If no {@link ProcedureParameter} are passed in, this property will default to
|
||||
* 'true'. This means that using a default {@link BeanPropertySqlParameterSourceFactory}
|
||||
* the bean properties of the payload will be used as a source for parameter values for
|
||||
* the to-be-executed Stored Procedure or Function.
|
||||
*
|
||||
* However, if {@link ProcedureParameter} are passed in, then this property
|
||||
* will by default evaluate to 'false'. {@link ProcedureParameter} allow for
|
||||
* SpEl Expressions to be provided and therefore it is highly beneficial to
|
||||
* have access to the entire {@link Message}.
|
||||
*
|
||||
* @param usePayloadAsParameterSource If false the entire {@link Message} is used as parameter source.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setUsePayloadAsParameterSource(boolean)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setUsePayloadAsParameterSource(boolean usePayloadAsParameterSource) {
|
||||
this.executor.setUsePayloadAsParameterSource(usePayloadAsParameterSource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,24 +17,13 @@
|
||||
package org.springframework.integration.jdbc;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.integration.MessageHandlingException;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.jdbc.storedproc.ProcedureParameter;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.SqlOutParameter;
|
||||
import org.springframework.jdbc.core.SqlParameter;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcCallOperations;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -48,27 +37,6 @@ public class StoredProcOutboundGateway extends AbstractReplyProducingMessageHand
|
||||
|
||||
private volatile boolean expectSingleResult = false;
|
||||
|
||||
/**
|
||||
* Constructor taking {@link DataSource} from which the DB Connection can be
|
||||
* obtained and the name of the stored procedure or function to
|
||||
* execute to retrieve new rows.
|
||||
*
|
||||
* @param dataSource used to create a {@link SimpleJdbcCall} instance
|
||||
* @param storedProcedureName Must not be null.
|
||||
*
|
||||
* @deprecated Since 2.2 use the constructor that expects a {@link StoredProcExecutor} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public StoredProcOutboundGateway(DataSource dataSource, String storedProcedureName) {
|
||||
|
||||
Assert.notNull(dataSource, "dataSource must not be null.");
|
||||
Assert.hasText(storedProcedureName, "storedProcedureName must not be null and cannot be empty.");
|
||||
|
||||
this.executor = new StoredProcExecutor(dataSource);
|
||||
this.executor.setStoredProcedureName(storedProcedureName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor taking {@link StoredProcExecutor}.
|
||||
*
|
||||
@@ -82,15 +50,6 @@ public class StoredProcOutboundGateway extends AbstractReplyProducingMessageHand
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies parameters, sets the parameters on {@link SimpleJdbcCallOperations}
|
||||
* and ensures the appropriate {@link SqlParameterSourceFactory} is defined
|
||||
* when {@link ProcedureParameter} are passed in.
|
||||
*/
|
||||
@Override
|
||||
protected void doInit() {
|
||||
};
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
|
||||
@@ -123,136 +82,6 @@ public class StoredProcOutboundGateway extends AbstractReplyProducingMessageHand
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the Stored Procedure or Stored Function to be executed.
|
||||
* If {@link StoredProcExecutor#isFunction} is set to "true", then this
|
||||
* property specifies the Stored Function name.
|
||||
*
|
||||
* Alternatively you can also specify the Stored Procedure name via
|
||||
* {@link StoredProcExecutor#setStoredProcedureNameExpression(Expression)}.
|
||||
*
|
||||
* @param storedProcedureName Must not be null and must not be empty
|
||||
*
|
||||
* @see StoredProcExecutor#setStoredProcedureNameExpression(Expression)
|
||||
* @see StoredProcExecutor#setStoredProcedureName(String)
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public void setStoredProcedureName(String storedProcedureName) {
|
||||
this.executor.setStoredProcedureName(storedProcedureName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicit declarations are necessary if the database you use is not a
|
||||
* Spring-supported database. Currently Spring supports metadata lookup of
|
||||
* stored procedure calls for the following databases:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Apache Derby</li>
|
||||
* <li>DB2</li>
|
||||
* <li>MySQL</li>
|
||||
* <li>Microsoft SQL Server</li>
|
||||
* <li>Oracle</li>
|
||||
* <li>Sybase</li>
|
||||
* <li>PostgreSQL</li>
|
||||
* </ul>
|
||||
* , ,
|
||||
* We also support metadata lookup of stored functions for the following
|
||||
* databases:
|
||||
*
|
||||
* <ul>
|
||||
* <li>MySQL</li>
|
||||
* <li>Microsoft SQL Server</li>
|
||||
* <li>Oracle</li>
|
||||
* <li>PostgreSQL</li>
|
||||
* </ul>
|
||||
*
|
||||
* See also: http://static.springsource.org/spring/docs/3.1.0.M2/spring-framework-reference/html/jdbc.html
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setSqlParameters(List)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSqlParameters(List<SqlParameter> sqlParameters) {
|
||||
this.executor.setSqlParameters(sqlParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does your stored procedure return one or more result sets? If so, you
|
||||
* can use the provided method for setting the respective RowMappers.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setReturningResultSetRowMappers(Map)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setReturningResultSetRowMappers(
|
||||
Map<String, RowMapper<?>> returningResultSetRowMappers) {
|
||||
this.executor.setReturningResultSetRowMappers(returningResultSetRowMappers);
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, the JDBC parameter definitions for the stored procedure are not
|
||||
* automatically derived from the underlying JDBC connection. In that case
|
||||
* you must pass in {@link SqlParameter} explicitly..
|
||||
*
|
||||
* @param ignoreColumnMetaData Defaults to <code>false</code>.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setIgnoreColumnMetaData(boolean)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setIgnoreColumnMetaData(boolean ignoreColumnMetaData) {
|
||||
this.executor.setIgnoreColumnMetaData(ignoreColumnMetaData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the procedure's return value should be included in the results
|
||||
* returned.
|
||||
*
|
||||
* @param returnValueRequired
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setReturnValueRequired(boolean)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setReturnValueRequired(boolean returnValueRequired) {
|
||||
this.executor.setReturnValueRequired(returnValueRequired);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom Stored Procedure parameters that may contain static values
|
||||
* or Strings representing an {@link Expression}.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setProcedureParameters(List)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setProcedureParameters(List<ProcedureParameter> procedureParameters) {
|
||||
this.executor.setProcedureParameters(procedureParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether a Stored Procedure or a Function is being executed.
|
||||
* The default value is false.
|
||||
*
|
||||
* @param isFunction If set to true an Sql Function is executed rather than a Stored Procedure.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setIsFunction(boolean)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setIsFunction(boolean isFunction) {
|
||||
this.executor.setIsFunction(isFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* This parameter indicates that only one result object shall be returned from
|
||||
* the Stored Procedure/Function Call. If set to true, a resultMap that contains
|
||||
@@ -277,73 +106,4 @@ public class StoredProcOutboundGateway extends AbstractReplyProducingMessageHand
|
||||
this.expectSingleResult = expectSingleResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the ability to set a custom {@link SqlParameterSourceFactory}.
|
||||
* Keep in mind that if {@link ProcedureParameter} are set explicitly and
|
||||
* you would like to provide a custom {@link SqlParameterSourceFactory},
|
||||
* then you must provide an instance of {@link ExpressionEvaluatingSqlParameterSourceFactory}.
|
||||
*
|
||||
* If not the SqlParameterSourceFactory will be replaced by the default
|
||||
* {@link ExpressionEvaluatingSqlParameterSourceFactory}.
|
||||
*
|
||||
* @param sqlParameterSourceFactory
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setSqlParameterSourceFactory(SqlParameterSourceFactory)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSqlParameterSourceFactory(SqlParameterSourceFactory sqlParameterSourceFactory) {
|
||||
this.executor.setSqlParameterSourceFactory(sqlParameterSourceFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to 'true', the payload of the Message will be used as a source for
|
||||
* providing parameters. If false the entire Message will be available as a
|
||||
* source for parameters.
|
||||
*
|
||||
* If no {@link ProcedureParameter} are passed in, this property will default to
|
||||
* 'true'. This means that using a default {@link BeanPropertySqlParameterSourceFactory}
|
||||
* the bean properties of the payload will be used as a source for parameter values for
|
||||
* the to-be-executed Stored Procedure or Function.
|
||||
*
|
||||
* However, if {@link ProcedureParameter} are passed in, then this property
|
||||
* will by default evaluate to 'false'. {@link ProcedureParameter} allow for
|
||||
* SpEl Expressions to be provided and therefore it is highly beneficial to
|
||||
* have access to the entire {@link Message}.
|
||||
*
|
||||
* @param usePayloadAsParameterSource If false the entire {@link Message} is used as parameter source.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setUsePayloadAsParameterSource(boolean)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setUsePayloadAsParameterSource(boolean usePayloadAsParameterSource) {
|
||||
this.executor.setUsePayloadAsParameterSource(usePayloadAsParameterSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this variable is set to <code>true</code> then all results from a stored
|
||||
* procedure call that don't have a corresponding {@link SqlOutParameter}
|
||||
* declaration will be bypassed.
|
||||
*
|
||||
* E.g. Stored Procedures may return an update count value, even though your
|
||||
* Stored Procedure only declared a single result parameter. The exact behavior
|
||||
* depends on the used database.
|
||||
*
|
||||
* The value is set on the underlying {@link JdbcTemplate}.
|
||||
*
|
||||
* Only few developers will probably ever like to process update counts, thus
|
||||
* the value defaults to <code>true</code>.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setSkipUndeclaredResults(boolean)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSkipUndeclaredResults(boolean skipUndeclaredResults) {
|
||||
this.executor.setSkipUndeclaredResults(skipUndeclaredResults);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,23 +16,13 @@
|
||||
package org.springframework.integration.jdbc;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.jdbc.storedproc.ProcedureParameter;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.SqlOutParameter;
|
||||
import org.springframework.jdbc.core.SqlParameter;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -50,26 +40,6 @@ public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport im
|
||||
|
||||
private volatile boolean expectSingleResult = false;
|
||||
|
||||
/**
|
||||
* Constructor taking {@link DataSource} from which the DB Connection can be
|
||||
* obtained and the stored procedure name to execute.
|
||||
*
|
||||
* @param dataSource used to create a {@link SimpleJdbcCall}
|
||||
* @param storedProcedureName Name of the Stored Procedure or Function to execute
|
||||
*
|
||||
* @deprecated Since 2.2 use the constructor that expects a {@link StoredProcExecutor} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public StoredProcPollingChannelAdapter(DataSource dataSource, String storedProcedureName) {
|
||||
|
||||
Assert.notNull(dataSource, "dataSource must not be null.");
|
||||
Assert.hasText(storedProcedureName, "storedProcedureName must not be null and cannot be empty.");
|
||||
|
||||
this.executor = new StoredProcExecutor(dataSource);
|
||||
this.executor.setStoredProcedureName(storedProcedureName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor taking {@link StoredProcExecutor}.
|
||||
*
|
||||
@@ -83,11 +53,6 @@ public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport im
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query. If a query result set contains one or more rows, the
|
||||
* Message payload will contain either a List of Maps for each row or, if a
|
||||
@@ -142,158 +107,11 @@ public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport im
|
||||
return this.executor.executeStoredProcedure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType(){
|
||||
return "stored-proc:inbound-channel-adapter";
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the Stored Procedure or Stored Function to be executed.
|
||||
* If {@link StoredProcExecutor#isFunction} is set to "true", then this
|
||||
* property specifies the Stored Function name.
|
||||
*
|
||||
* Alternatively you can also specify the Stored Procedure name via
|
||||
* {@link StoredProcExecutor#setStoredProcedureNameExpression(Expression)}.
|
||||
*
|
||||
* @param storedProcedureName Must not be null and must not be empty
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setStoredProcedureName(String)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setStoredProcedureName(String storedProcedureName) {
|
||||
this.executor.setStoredProcedureName(storedProcedureName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the ability to set a custom {@link SqlParameterSourceFactory}.
|
||||
* Keep in mind that if {@link ProcedureParameter} are set explicitly and
|
||||
* you would like to provide a custom {@link SqlParameterSourceFactory},
|
||||
* then you must provide an instance of {@link ExpressionEvaluatingSqlParameterSourceFactory}.
|
||||
*
|
||||
* If not the SqlParameterSourceFactory will be replaced by the default
|
||||
* {@link ExpressionEvaluatingSqlParameterSourceFactory}.
|
||||
*
|
||||
* @param sqlParameterSourceFactory
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setSqlParameterSourceFactory(SqlParameterSourceFactory)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSqlParameterSourceFactory(SqlParameterSourceFactory sqlParameterSourceFactory) {
|
||||
this.executor.setSqlParameterSourceFactory(sqlParameterSourceFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicit declarations are necessary if the database you use is not a
|
||||
* Spring-supported database. Currently Spring supports metadata lookup of
|
||||
* stored procedure calls for the following databases:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Apache Derby</li>
|
||||
* <li>DB2</li>
|
||||
* <li>MySQL</li>
|
||||
* <li>Microsoft SQL Server</li>
|
||||
* <li>Oracle</li>
|
||||
* <li>Sybase</li>
|
||||
* <li>PostgreSQL</li>
|
||||
* </ul>
|
||||
* , ,
|
||||
* We also support metadata lookup of stored functions for the following
|
||||
* databases:
|
||||
*
|
||||
* <ul>
|
||||
* <li>MySQL</li>
|
||||
* <li>Microsoft SQL Server</li>
|
||||
* <li>Oracle</li>
|
||||
* <li>PostgreSQL</li>
|
||||
* </ul>
|
||||
*
|
||||
* See also: http://static.springsource.org/spring/docs/3.1.0.M2/spring-framework-reference/html/jdbc.html
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setSqlParameters(List)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSqlParameters(List<SqlParameter> sqlParameters) {
|
||||
this.executor.setSqlParameters(sqlParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does your stored procedure return one or more result sets? If so, you
|
||||
* can use the provided method for setting the respective Rowmappers.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setReturningResultSetRowMappers(Map)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setReturningResultSetRowMappers(
|
||||
Map<String, RowMapper<?>> returningResultSetRowMappers) {
|
||||
this.executor.setReturningResultSetRowMappers(returningResultSetRowMappers);
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, the JDBC parameter definitions for the stored procedure are not
|
||||
* automatically derived from the underlying JDBC connection. In that case
|
||||
* you must pass in {@link SqlParameter} explicitly..
|
||||
*
|
||||
* @param ignoreColumnMetaData Defaults to <code>false</code>.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setIgnoreColumnMetaData(boolean)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setIgnoreColumnMetaData(boolean ignoreColumnMetaData) {
|
||||
this.executor.setIgnoreColumnMetaData(ignoreColumnMetaData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the procedure's return value should be included in the results
|
||||
* returned.
|
||||
*
|
||||
* @param returnValueRequired
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setReturnValueRequired(boolean)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setReturnValueRequired(boolean returnValueRequired) {
|
||||
this.executor.setReturnValueRequired(returnValueRequired);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom Stored Procedure parameters that may contain static values
|
||||
* or Strings representing an {@link Expression}.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setProcedureParameters(List)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setProcedureParameters(List<ProcedureParameter> procedureParameters) {
|
||||
this.executor.setProcedureParameters(procedureParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether a Stored Procedure or a Function is being executed.
|
||||
* The default value is false.
|
||||
*
|
||||
* @param isFunction If set to true an Sql Function is executed rather than a Stored Procedure.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setIsFunction(boolean)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setFunction(boolean isFunction) {
|
||||
this.executor.setIsFunction(isFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* This parameter indicates that only one result object shall be returned from
|
||||
* the Stored Procedure/Function Call. If set to true, a resultMap that contains
|
||||
@@ -318,28 +136,4 @@ public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport im
|
||||
this.expectSingleResult = expectSingleResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this variable is set to <code>true</code> then all results from a stored
|
||||
* procedure call that don't have a corresponding {@link SqlOutParameter}
|
||||
* declaration will be bypassed.
|
||||
*
|
||||
* E.g. Stored Procedures may return an update count value, even though your
|
||||
* Stored Procedure only declared a single result parameter. The exact behavior
|
||||
* depends on the used database.
|
||||
*
|
||||
* The value is set on the underlying {@link JdbcTemplate}.
|
||||
*
|
||||
* Only few developers will probably ever like to process update counts, thus
|
||||
* the value defaults to <code>true</code>.
|
||||
*
|
||||
* @deprecated Since 2.2 set the respective property on the passed-in {@link StoredProcExecutor}
|
||||
*
|
||||
* @see StoredProcExecutor#setSkipUndeclaredResults(boolean)
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSkipUndeclaredResults(boolean skipUndeclaredResults) {
|
||||
this.executor.setSkipUndeclaredResults(skipUndeclaredResults);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<bean id="storedProcExecutor" class="org.springframework.integration.jdbc.StoredProcExecutor">
|
||||
<constructor-arg name="dataSource" ref="dataSource" />
|
||||
<property name="storedProcedureName" value="GET_PRIME_NUMBERS"/>
|
||||
<property name="function" value="false"/>
|
||||
<property name="isFunction" value="false"/>
|
||||
<property name="procedureParameters" >
|
||||
<util:list>
|
||||
<bean class="org.springframework.integration.jdbc.storedproc.ProcedureParameter">
|
||||
|
||||
@@ -178,10 +178,15 @@ public class RedisQueueMessageDrivenEndpoint extends MessageProducerSupport impl
|
||||
value = this.boundListOperations.rightPop(this.receiveTimeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Failed to execute listening task. Will attempt to resubmit in " + this.recoveryInterval + " milliseconds.", e);
|
||||
this.listening = false;
|
||||
this.sleepBeforeRecoveryAttempt();
|
||||
this.publishException(e);
|
||||
if (this.active) {
|
||||
logger.error("Failed to execute listening task. Will attempt to resubmit in " + this.recoveryInterval + " milliseconds.", e);
|
||||
this.publishException(e);
|
||||
this.sleepBeforeRecoveryAttempt();
|
||||
}
|
||||
else {
|
||||
logger.debug("Failed to execute listening task. " + e.getClass() + ": " + e.getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -267,6 +267,4 @@ public class XPathRouterParserTests {
|
||||
}
|
||||
}
|
||||
|
||||
public static class MyChannelResolver extends BeanFactoryChannelResolver{}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,34 +8,34 @@
|
||||
http://www.springframework.org/schema/integration/xml http://www.springframework.org/schema/integration/xml/spring-integration-xml.xsd">
|
||||
|
||||
<int-xml:xpath-router id="xpathRouterEmpty" input-channel="xpathRouterEmptyChannel">
|
||||
<int-xml:xpath-expression expression="/name"/>
|
||||
</int-xml:xpath-router>
|
||||
|
||||
<int-xml:xpath-router id="xpathRouterAsString" input-channel="xpathStringChannel" evaluate-as-string="true">
|
||||
<int-xml:xpath-expression expression="name(./node())"/>
|
||||
</int-xml:xpath-router>
|
||||
|
||||
<int-xml:xpath-router id="xpathRouterWithMapping" input-channel="xpathRouterWithMappingChannel">
|
||||
<int-xml:xpath-expression expression="/name"/>
|
||||
<int-xml:mapping value="channelA" channel="channelB"/>
|
||||
</int-xml:xpath-router>
|
||||
|
||||
<int-xml:xpath-router id="xpathRouterWithMappingMultiChannel" input-channel="multiChannelRouterChannel">
|
||||
<int-xml:xpath-expression expression="/root/name" />
|
||||
<int-xml:mapping value="channelA" channel="channelA"/>
|
||||
<int-xml:mapping value="channelB" channel="channelA"/>
|
||||
</int-xml:xpath-router>
|
||||
|
||||
<int-xml:xpath-router id="xpathRouterWithCustomConverter"
|
||||
input-channel="customConverterChannel" evaluate-as-string="true"
|
||||
converter="testConverter" channel-resolver="cr">
|
||||
<int-xml:xpath-router id="xpathRouterAsString" input-channel="xpathStringChannel" evaluate-as-string="true">
|
||||
<int-xml:xpath-expression expression="name(./node())"/>
|
||||
</int-xml:xpath-router>
|
||||
|
||||
<int-xml:xpath-router id="xpathRouterWithMapping" input-channel="xpathRouterWithMappingChannel">
|
||||
<int-xml:xpath-expression expression="/name"/>
|
||||
<int-xml:mapping value="channelA" channel="channelB"/>
|
||||
</int-xml:xpath-router>
|
||||
|
||||
<int-xml:xpath-router id="xpathRouterWithMappingMultiChannel" input-channel="multiChannelRouterChannel">
|
||||
<int-xml:xpath-expression expression="/root/name" />
|
||||
<int-xml:mapping value="channelA" channel="channelA"/>
|
||||
<int-xml:mapping value="channelB" channel="channelA"/>
|
||||
</int-xml:xpath-router>
|
||||
|
||||
<int-xml:xpath-router id="xpathRouterWithCustomConverter"
|
||||
input-channel="customConverterChannel" evaluate-as-string="true"
|
||||
converter="testConverter">
|
||||
<int-xml:xpath-expression expression="/name"/>
|
||||
</int-xml:xpath-router>
|
||||
|
||||
|
||||
<int:channel id="channelA">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
|
||||
<int:channel id="channelB">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
@@ -46,5 +46,4 @@
|
||||
|
||||
<bean id="testConverter" class="org.springframework.integration.xml.config.XPathRouterParserTests$TestXmlPayloadConverter"/>
|
||||
|
||||
<bean id="cr" class="org.springframework.integration.xml.config.XPathRouterParserTests.MyChannelResolver"/>
|
||||
</beans>
|
||||
|
||||
@@ -33,22 +33,10 @@ public class XmppHeaders {
|
||||
|
||||
public static final String TO = PREFIX + "to";
|
||||
|
||||
/**
|
||||
* {@link Deprecated} use {@link #TO} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String CHAT_TO = TO;
|
||||
|
||||
public static final String FROM = PREFIX + "from";
|
||||
|
||||
public static final String THREAD = PREFIX + "thread";
|
||||
|
||||
/**
|
||||
* {@link Deprecated} use {@link #THREAD} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String CHAT_THREAD_ID = THREAD;
|
||||
|
||||
public static final String SUBJECT = PREFIX + "subject";
|
||||
|
||||
public static final String TYPE = PREFIX + "type";
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.integration.xmpp.core;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.integration.endpoint.MessageProducerSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -43,15 +42,6 @@ public abstract class AbstractXmppConnectionAwareEndpoint extends MessageProduce
|
||||
this.xmppConnection = xmppConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Deprecated} This method will be eligible for removal in 2.1.
|
||||
* Use {@link #setOutputChannel(MessageChannel)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setRequestChannel(MessageChannel requestChannel) {
|
||||
this.setOutputChannel(requestChannel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
|
||||
@@ -282,7 +282,7 @@ this list can also be simple patterns to be matched against the header names (e.
|
||||
</important>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="amqp-outbound-channel-adapter">
|
||||
<title>Outbound Channel Adapter</title>
|
||||
|
||||
<para>A configuration sample for an AMQP Outbound Channel Adapter is shown
|
||||
@@ -378,7 +378,7 @@ this list can also be simple patterns to be matched against the header names (e.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="amqp-inbound-gateway">
|
||||
<title>Inbound Gateway</title>
|
||||
<para>A configuration sample for an AMQP Inbound Gateway is shown
|
||||
below.</para>
|
||||
@@ -434,7 +434,7 @@ this list can also be simple patterns to be matched against the header names (e.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="amqp-outbound-gateway">
|
||||
<title>Outbound Gateway</title>
|
||||
<para>A configuration sample for an AMQP Outbound Gateway is shown
|
||||
below.</para>
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
</section>
|
||||
|
||||
<section id="channel-adapter-namespace-outbound">
|
||||
<title>Configuring Outbound Channel Adapter</title>
|
||||
<title>Configuring An Outbound Channel Adapter</title>
|
||||
<para>
|
||||
An "outbound-channel-adapter" element can also connect a <interfacename>MessageChannel</interfacename> to any POJO consumer
|
||||
method that should be invoked with the payload of Messages sent to that channel.
|
||||
|
||||
@@ -199,17 +199,27 @@
|
||||
</para>
|
||||
<para>
|
||||
The <classname>DirectChannel</classname> internally delegates to a Message Dispatcher to invoke its
|
||||
subscribed Message Handlers, and that dispatcher can have a load-balancing strategy. The load-balancer
|
||||
determines how invocations will be ordered in the case that there are multiple handlers subscribed to the
|
||||
same channel. When using the namespace support described below, the default strategy is
|
||||
"round-robin" which essentially load-balances across the handlers in rotation.
|
||||
<note>
|
||||
The "round-robin" strategy is currently the only implementation available out-of-the-box in Spring
|
||||
Integration. Other strategy implementations may be added in future versions.
|
||||
</note>
|
||||
subscribed Message Handlers, and that dispatcher can have a load-balancing strategy exposed via
|
||||
<emphasis>load-balancer</emphasis> or <emphasis>load-balancer-ref</emphasis> attributes (mutually exclusive). The load balancing strategy
|
||||
is used by the Message Dispatcher to help determine how Messages are distributed amongst Message Handlers
|
||||
in the case that there are multiple Message Handlers subscribed to the same channel.
|
||||
As a convinience the <emphasis>load-balancer</emphasis> attribute exposes enumeration of values pointing to pre-existing implementations
|
||||
of <classname>LoadBalancingStrategy</classname>.
|
||||
The "round-robin" (load-balances across the handlers in rotation) and "none" (for the cases where one wants to explicitely disable load balancing)
|
||||
are the only available values.
|
||||
Other strategy implementations may be added in future versions.
|
||||
However, since version 3.0 you can provide your own implementation of the <classname>LoadBalancingStrategy</classname> and
|
||||
inject it using <emphasis>load-balancer-ref</emphasis> attribute which should point to a bean that implements
|
||||
<classname>LoadBalancingStrategy</classname>.
|
||||
<programlisting language="xml"><![CDATA[<int:channel id="lbRefChannel">
|
||||
<int:dispatcher load-balancer-ref="lb"/>
|
||||
</int:channel>
|
||||
|
||||
<bean id="lb" class="foo.bar.SampleLoadBalancingStrategy"/>]]></programlisting>
|
||||
Note that <emphasis>load-balancer</emphasis> or <emphasis>load-balancer-ref</emphasis> attributes are mutually exclusive.
|
||||
</para>
|
||||
<para>
|
||||
The load-balancer also works in combination with a boolean <emphasis>failover</emphasis> property.
|
||||
The load-balancing also works in combination with a boolean <emphasis>failover</emphasis> property.
|
||||
If the "failover" value is true (the default), then the dispatcher will fall back to any subsequent
|
||||
handlers as necessary when preceding handlers throw Exceptions. The order is determined by an optional
|
||||
order value defined on the handlers themselves or, if no such value exists, the order in which the
|
||||
@@ -539,8 +549,8 @@ payload to an Integer.
|
||||
<code>message-store</code> attribute as shown in the next example.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:channel id="dbBackedChannel">
|
||||
<int:queue message-store="messageStore">
|
||||
<int:channel id="myChannel">
|
||||
<int:queue message-store="messageStore"/>
|
||||
</int:channel>
|
||||
|
||||
<int-jdbc:message-store id="messageStore" data-source="someDataSource"/>]]></programlisting>
|
||||
|
||||
|
||||
225
src/reference/docbook/endpoint-summary.xml
Normal file
225
src/reference/docbook/endpoint-summary.xml
Normal file
@@ -0,0 +1,225 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="endpoint-summary"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Endpoint Quick Reference Table</title>
|
||||
<para>
|
||||
As discussed in the sections above, Spring Integration provides a number of endpoints used
|
||||
to interface with external systems, file systems etc. The following is a summary of the various
|
||||
endpoints with quick links to the appropriate chapter.
|
||||
</para>
|
||||
<para>
|
||||
To recap, <emphasis role="bold">Inbound Channel Adapters</emphasis> are used for one-way integration
|
||||
bringing data into the messagng application. <emphasis role="bold">Outbound Channel Adapters</emphasis>
|
||||
are used for one-way integration to send data out of the messaging application.
|
||||
<emphasis role="bold">Inbound Gateways</emphasis> are used for a bidirectional integration flow where
|
||||
some other system invokes the messaging application and receives a reply.
|
||||
<emphasis role="bold">Outbound Gateways</emphasis> are used for a bidirectional integration flow where
|
||||
the messaging application invokes some external service or entity, expecting a result.
|
||||
</para>
|
||||
<table id="endpoint-summary-tbl">
|
||||
<tgroup cols="5">
|
||||
<colspec colnum="1" colname="col1" colwidth="1*"/>
|
||||
<colspec colnum="2" colname="col2" colwidth="1*"/>
|
||||
<colspec colnum="3" colname="col3" colwidth="1*"/>
|
||||
<colspec colnum="4" colname="col4" colwidth="1*"/>
|
||||
<colspec colnum="5" colname="col5"/>
|
||||
<thead>
|
||||
<row>
|
||||
<entry align="center">Module</entry>
|
||||
<entry align="center">Inbound Adapter</entry>
|
||||
<entry align="center">Outbound Adapter</entry>
|
||||
<entry align="center">Inbound Gateway</entry>
|
||||
<entry align="center">Outbound Gateway</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><emphasis role="bold">AMQP</emphasis></entry>
|
||||
<entry><xref linkend="amqp-inbound-channel-adapter" /></entry>
|
||||
<entry><xref linkend="amqp-outbound-channel-adapter" /></entry>
|
||||
<entry><xref linkend="amqp-inbound-gateway" /></entry>
|
||||
<entry><xref linkend="amqp-outbound-gateway" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">Events</emphasis></entry>
|
||||
<entry><xref linkend="applicationevent-inbound" /></entry>
|
||||
<entry><xref linkend="applicationevent-outbound" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">Feed</emphasis></entry>
|
||||
<entry><xref linkend="feed-inbound-channel-adapter" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">File</emphasis></entry>
|
||||
<entry><xref linkend="file-reading" /> and <xref linkend="file-tailing" /></entry>
|
||||
<entry><xref linkend="file-writing" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry><xref linkend="file-writing" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">FTP(S)</emphasis></entry>
|
||||
<entry><xref linkend="ftp-inbound" /></entry>
|
||||
<entry><xref linkend="ftp-outbound" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry><xref linkend="ftp-outbound-gateway" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">Gemfire</emphasis></entry>
|
||||
<entry><xref linkend="gemfire-inbound" /> and <xref linkend="gemfire-cq" /></entry>
|
||||
<entry><xref linkend="gemfire-outbound" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">HTTP</emphasis></entry>
|
||||
<entry><xref linkend="http-namespace" /></entry>
|
||||
<entry><xref linkend="http-namespace" /></entry>
|
||||
<entry><xref linkend="http-inbound" /></entry>
|
||||
<entry><xref linkend="http-outbound" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">JDBC</emphasis></entry>
|
||||
<entry><xref linkend="jdbc-inbound-channel-adapter" /> and <xref linkend="stored-procedure-inbound-channel-adapter" /></entry>
|
||||
<entry><xref linkend="jdbc-outbound-channel-adapter" /> and <xref linkend="stored-procedure-outbound-channel-adapter" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry><xref linkend="jdbc-outbound-gateway" /> and <xref linkend="stored-procedure-outbound-gateway" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">JMS</emphasis></entry>
|
||||
<entry><xref linkend="jms-inbound-channel-adapter" /> and <xref linkend="jms-message-driven-channel-adapter" /></entry>
|
||||
<entry><xref linkend="jms-outbound-channel-adapter" /></entry>
|
||||
<entry><xref linkend="jms-inbound-gateway" /></entry>
|
||||
<entry><xref linkend="jms-outbound-gateway" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">JMX</emphasis></entry>
|
||||
<entry><xref linkend="jmx-notification-listening-channel-adapter" /> and
|
||||
<xref linkend="jmx-attribute-polling-channel-adapter" /> and
|
||||
<xref linkend="tree-polling-channel-adapter" /></entry>
|
||||
<entry><xref linkend="jmx-notification-publishing-channel-adapter" /> and
|
||||
<xref linkend="jmx-operation-invoking-channel-adapter" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry><xref linkend="jmx-operation-invoking-outbound-gateway" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">JPA</emphasis></entry>
|
||||
<entry><xref linkend="jpa-inbound-channel-adapter" /></entry>
|
||||
<entry><xref linkend="jpa-outbound-channel-adapter" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry><xref linkend="jpa-updating-outbound-gateway" /> and <xref linkend="jpa-retrieving-outbound-gateway" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">Mail</emphasis></entry>
|
||||
<entry><xref linkend="mail-inbound" /></entry>
|
||||
<entry><xref linkend="mail-outbound" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">MongoDB</emphasis></entry>
|
||||
<entry><xref linkend="mongodb-inbound-channel-adapter" /></entry>
|
||||
<entry><xref linkend="mongodb-outbound-channel-adapter" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">Redis</emphasis></entry>
|
||||
<entry><xref linkend="redis-inbound-channel-adapter" /> and
|
||||
<xref linkend="redis-queue-inbound-channel-adapter" /> and
|
||||
<xref linkend="redis-store-inbound-channel-adapter" /></entry>
|
||||
<entry><xref linkend="redis-outbound-channel-adapter" /> and
|
||||
<xref linkend="redis-queue-outbound-channel-adapter" /> and
|
||||
<xref linkend="redis-store-outbound-channel-adapter" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">Resource</emphasis></entry>
|
||||
<entry><xref linkend="resource-inbound-channel-adapter" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">RMI</emphasis></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
<entry><xref linkend="rmi-inbound" /></entry>
|
||||
<entry><xref linkend="rmi-outbound" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">SFTP</emphasis></entry>
|
||||
<entry><xref linkend="sftp-inbound" /></entry>
|
||||
<entry><xref linkend="sftp-outbound" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry><xref linkend="sftp-outbound-gateway" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">Stream</emphasis></entry>
|
||||
<entry><xref linkend="stream-reading" /></entry>
|
||||
<entry><xref linkend="stream-writing" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">Syslog</emphasis></entry>
|
||||
<entry><xref linkend="syslog-inbound-adapter" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">TCP</emphasis></entry>
|
||||
<entry><xref linkend="tcp-adapters" /></entry>
|
||||
<entry><xref linkend="tcp-adapters" /></entry>
|
||||
<entry><xref linkend="tcp-gateways" /></entry>
|
||||
<entry><xref linkend="tcp-gateways" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">Twitter</emphasis></entry>
|
||||
<entry><xref linkend="twitter-inbound" /></entry>
|
||||
<entry><xref linkend="twitter-outbound" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">UDP</emphasis></entry>
|
||||
<entry><xref linkend="udp-adapters" /></entry>
|
||||
<entry><xref linkend="udp-adapters" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">Web Services</emphasis></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
<entry><xref linkend="webservices-inbound" /></entry>
|
||||
<entry><xref linkend="webservices-outbound" /></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><emphasis role="bold">XMPP</emphasis></entry>
|
||||
<entry><xref linkend="xmpp-messages" /> and <xref linkend="xmpp-presence" /></entry>
|
||||
<entry><xref linkend="xmpp-messages" /> and <xref linkend="xmpp-presence" /></entry>
|
||||
<entry>N</entry>
|
||||
<entry>N</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
<para>
|
||||
In addition, as discussed in <xref linkend="spring-integration-core-messaging" />, endpoints are provided
|
||||
for interfacing with Plain Old Java Objects
|
||||
(POJOs). As discussed in <xref linkend="channel-adapter" />, the <code><int:inbound-channel-adapter></code>
|
||||
allows polling a java method for data; the
|
||||
<code><int:outbound-channel-adapter></code> allows sending data to a <code>void</code> method, and
|
||||
as discussed in <xref linkend="gateway" />, the <code><int:gateway></code> allows any Java program
|
||||
to invoke a messaging flow. Each of these without requiring any source level
|
||||
dependencies on Spring Integration. The equivalent of an outbound gateway in this context would be to use a
|
||||
<xref linkend="service-activator" /> to invoke a method that returns an Object of some kind.
|
||||
</para>
|
||||
</chapter>
|
||||
@@ -62,55 +62,12 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/feed
|
||||
Spring Integration provides a convenient mechanism to eliminate the need to worry about duplicate entries.
|
||||
Each feed entry will have a <emphasis>published date</emphasis> field. Every time a new Message is generated and sent,
|
||||
Spring Integration will store the value of the latest <emphasis>published date</emphasis> in an instance of the
|
||||
<classname>org.springframework.integration.metadata.MetadataStore</classname> strategy. The MetadataStore interface is
|
||||
designed to store various types of generic meta-data (e.g., published date of the last feed entry that has been processed)
|
||||
to help components such as this Feed adapter deal with duplicates.
|
||||
<interfacename>MetadataStore</interfacename> strategy (<xref linkend="metadata-store"/>).
|
||||
</para>
|
||||
<para>
|
||||
The default rule for locating this metadata store is as follows:
|
||||
<emphasis>Spring Integration</emphasis> will look for a bean of type
|
||||
<classname>org.springframework.integration.metadata.MetadataStore</classname> in
|
||||
the ApplicationContext. If one is found then it will be used, otherwise
|
||||
it will create a new instance of <classname>SimpleMetadataStore</classname>
|
||||
which is an in-memory implementation that will only persist metadata within
|
||||
the lifecycle of the currently running Application Context. This means
|
||||
that upon restart you may end up with duplicate entries.
|
||||
</para>
|
||||
<para>
|
||||
If you need to persist metadata between Application Context restarts, two
|
||||
persistent <interfacename>MetadataStores</interfacename> are available:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>PropertiesPersistingMetadataStore</listitem>
|
||||
<listitem>RedisMetadataStore</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
The <classname>PropertiesPersistingMetadataStore</classname> is backed by
|
||||
a properties file and a
|
||||
<interfacename><ulink url="http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/PropertiesPersister.html">PropertiesPersister</ulink></interfacename>.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<bean id="metadataStore"
|
||||
class="org.springframework.integration.store.PropertiesPersistingMetadataStore"/>]]></programlisting>
|
||||
<para>
|
||||
As of <emphasis>Spring Integration 3.0</emphasis> a Redis-based
|
||||
<interfacename>MetadataStore</interfacename> is also available. For
|
||||
more information regarding the <classname>RedisMetadataStore</classname>
|
||||
see <xref linkend="redis-metadata-store" />.
|
||||
</para>
|
||||
<warning>
|
||||
Be careful when using the same Redis instancce across multiple application
|
||||
contexts as separate Feed adapters may accidentally use the same persisted
|
||||
key.
|
||||
</warning>
|
||||
<para>
|
||||
Alternatively, you could provide your own implementation of the
|
||||
<interfacename>MetadataStore</interfacename> interface (e.g. JdbcMetadataStore)
|
||||
and configure it as bean in the Application Context.
|
||||
</para>
|
||||
<note>
|
||||
The key used to persist the latest <emphasis>published date</emphasis> is the value of the (required)
|
||||
<code>id</code> attribute of the Feed Inbound Channel Adapter component plus the <code>feedUrl</code>
|
||||
from the adapter's configuration.
|
||||
</note>
|
||||
<note>
|
||||
The key used to persist the latest <emphasis>published date</emphasis> is the value of the (required)
|
||||
<code>id</code> attribute of the Feed Inbound Channel Adapter component plus the <code>feedUrl</code>
|
||||
from the adapter's configuration.
|
||||
</note>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
@@ -39,9 +39,8 @@
|
||||
The <classname>AcceptOnceFileListFilter</classname> stores its state in memory. If you wish the
|
||||
state to survive a system restart, consider using the
|
||||
<classname>FileSystemPersistentAcceptOnceFileListFilter</classname> instead. This filter stores
|
||||
the accepted file names in a <interfacename>MetadataStore</interfacename>. The framework supplies
|
||||
several store implementations (such as Redis), or you can provide your own. This filter matches on
|
||||
the filename and modified time.
|
||||
the accepted file names in a <interfacename>MetadataStore</interfacename> strategy (<xref linkend="metadata-store"/>).
|
||||
This filter matches on the filename and modified time.
|
||||
</note>
|
||||
<programlisting language="xml"><![CDATA[<bean id="pollableFileSource"
|
||||
class="org.springframework.integration.file.FileReadingMessageSource"
|
||||
|
||||
@@ -192,10 +192,10 @@ protected void postProcessClientBeforeConnect(T client) throws IOException {
|
||||
The <classname>AcceptOnceFileListFilter</classname> stores its state in memory. If you wish the
|
||||
state to survive a system restart, consider using the
|
||||
<classname>FtpPersistentAcceptOnceFileListFilter</classname> instead. This filter stores
|
||||
the accepted file names in a <interfacename>MetadataStore</interfacename>. The framework supplies
|
||||
several store implementations (such as Redis), or you can provide your own. This filter matches on
|
||||
the filename and the remote modified time.
|
||||
</para>
|
||||
the accepted file names in an instance of the
|
||||
<interfacename>MetadataStore</interfacename> strategy (<xref linkend="metadata-store"/>).
|
||||
This filter matches on the filename and the remote modified time.
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
Beginning with <emphasis>version 3.0</emphasis>, you can also specify a filter used to filter the files locally, once they have
|
||||
@@ -208,8 +208,8 @@ protected void postProcessClientBeforeConnect(T client) throws IOException {
|
||||
The <classname>AcceptOnceFileListFilter</classname> stores its state in memory. If you wish the
|
||||
state to survive a system restart, consider using the
|
||||
<classname>FileSystemPersistentAcceptOnceFileListFilter</classname> as a local filter instead. This filter stores
|
||||
the accepted file names in a <interfacename>MetadataStore</interfacename>. The framework supplies
|
||||
several store implementations (such as Redis), or you can provide your own.
|
||||
the accepted file names in an instance of the
|
||||
<interfacename>MetadataStore</interfacename> strategy (<xref linkend="metadata-store"/>).
|
||||
<important>
|
||||
This filter compares the filename and modified timestamp. If you wish to use this technique to avoid a
|
||||
re-synchronized file from being processed, you should use the <code>preserve-timestamp</code> attribute discussed above.
|
||||
|
||||
@@ -28,7 +28,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/gemfire
|
||||
http://www.springframework.org/schema/integration/gemfire/spring-integration-gemfire.xsd"]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<section id="gemfire-inbound">
|
||||
<title>Inbound Channel Adapter</title>
|
||||
<para>
|
||||
The <emphasis>inbound-channel-adapter</emphasis> produces messages on a channel triggered by a GemFire <classname>EntryEvent</classname>. GemFire
|
||||
@@ -49,7 +49,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/gemfire
|
||||
If <code>expression</code> is not provided the message payload will be a GemFire <classname>EntryEvent</classname>
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<section id="gemfire-cq">
|
||||
<title>Continuous Query Inbound Channel Adapter</title>
|
||||
<para>
|
||||
The <emphasis>cq-inbound-channel-adapter</emphasis> produces messages a channel triggered by a GemFire continuous query or <classname>CqEvent</classname> event. Spring GemFire introduced
|
||||
@@ -90,7 +90,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/gemfire
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="gemfire-outbound">
|
||||
<title>Outbound Channel Adapter</title>
|
||||
<para>
|
||||
The <emphasis>outbound-channel-adapter</emphasis> writes cache entries mapped from the message payload. In its simplest form, it expects a
|
||||
|
||||
@@ -295,10 +295,11 @@ Caused by: java.lang.RuntimeException: foo
|
||||
<para>
|
||||
Spring Retry has a great deal of flexibility for determining which
|
||||
exceptions can invoke retry. The default configuration will retry
|
||||
for all exceptions. Given that, user exceptions may be wrapped in
|
||||
a <classname>MessagingException</classname> in the underlying handler,
|
||||
we need to ensure that the classification examines the exception causes.
|
||||
The default classifier just looks at the top level exception.
|
||||
for all exceptions and the exception classifier just looks at the
|
||||
top level exception. If you configure it to, say, only retry
|
||||
on <classname>BarException</classname> and your application throws
|
||||
a <classname>FooException</classname> where the cause is a
|
||||
<classname>BarException</classname>, retry will not occur.
|
||||
</para>
|
||||
<para>
|
||||
Since <emphasis>Spring Retry 1.0.3</emphasis>, the
|
||||
|
||||
@@ -115,12 +115,13 @@
|
||||
<xi:include href="./system-management.xml"/>
|
||||
</part>
|
||||
<part id="spring-integration-adapters">
|
||||
<title>Integration Adapters</title>
|
||||
<title>Integration Endpoints</title>
|
||||
<partintro id="spring-integration-adapters">
|
||||
<para>This section covers the various Channel Adapters and Messaging Gateways provided
|
||||
by Spring Integration to support Message-based communication with external systems.
|
||||
</para>
|
||||
</partintro>
|
||||
<xi:include href="./endpoint-summary.xml"/>
|
||||
<xi:include href="./amqp.xml"/>
|
||||
<xi:include href="./event.xml"/>
|
||||
<xi:include href="./feed.xml"/>
|
||||
@@ -128,7 +129,6 @@
|
||||
<xi:include href="./ftp.xml"/>
|
||||
<xi:include href="./gemfire.xml"/>
|
||||
<xi:include href="./http.xml"/>
|
||||
<xi:include href="./ip.xml"/>
|
||||
<xi:include href="./jdbc.xml"/>
|
||||
<xi:include href="./jpa.xml"/>
|
||||
<xi:include href="./jms.xml"/>
|
||||
@@ -140,6 +140,7 @@
|
||||
<xi:include href="./sftp.xml"/>
|
||||
<xi:include href="./stream.xml"/>
|
||||
<xi:include href="./syslog.xml"/>
|
||||
<xi:include href="./ip.xml"/>
|
||||
<xi:include href="./twitter.xml"/>
|
||||
<xi:include href="./ws.xml"/>
|
||||
<xi:include href="./xml.xml"/>
|
||||
|
||||
68
src/reference/docbook/meta-data-store.xml
Normal file
68
src/reference/docbook/meta-data-store.xml
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<section xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="metadata-store"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Metadata Store</title>
|
||||
<para>
|
||||
Many external systems, services or resources aren't transactional (Twitter, RSS, file system etc.)
|
||||
and there is no any ability to mark the data as read. Or there is just need to implement the
|
||||
Enterprise Integration Pattern <ulink url="http://eaipatterns.com/IdempotentReceiver.html">Idempotent Receiver</ulink>
|
||||
in some integration solutions. To achieve this goal and store some previous state of the Endpoint before the next
|
||||
interaction with external system, or deal with the next Message, Spring Integration provides the <emphasis>Metadata Store</emphasis>
|
||||
component being an implementation of the <interfacename>org.springframework.integration.metadata.MetadataStore</interfacename>
|
||||
interface with a general <emphasis>key-value</emphasis> contract.
|
||||
</para>
|
||||
<para>
|
||||
The <emphasis>Metadata Store</emphasis> is designed to store various types of generic meta-data
|
||||
(e.g., published date of the last feed entry that has been processed) to help components such as the Feed adapter deal with duplicates.
|
||||
If a component is not directly provided with a reference to a <interfacename>MetadataStore</interfacename>,
|
||||
the algorithm for locating a metadata store is as follows: First, look for a bean with id
|
||||
<code>metadataStore</code> in the ApplicationContext. If one is found then it will be used, otherwise
|
||||
it will create a new instance of <classname>SimpleMetadataStore</classname> which is an in-memory implementation
|
||||
that will only persist metadata within the lifecycle of the currently running Application Context. This means
|
||||
that upon restart you may end up with duplicate entries.
|
||||
</para>
|
||||
<para>
|
||||
If you need to persist metadata between Application Context restarts, two
|
||||
persistent <interfacename>MetadataStores</interfacename> are provided by the framework:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>PropertiesPersistingMetadataStore</listitem>
|
||||
<listitem><xref linkend="redis-metadata-store"/></listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
The <classname>PropertiesPersistingMetadataStore</classname> is backed by a properties file and a
|
||||
<interfacename><ulink url="http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/PropertiesPersister.html">PropertiesPersister</ulink></interfacename>.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<bean id="metadataStore"
|
||||
class="org.springframework.integration.store.PropertiesPersistingMetadataStore"/>]]></programlisting>
|
||||
<para>
|
||||
Alternatively, you can provide your own implementation of the
|
||||
<interfacename>MetadataStore</interfacename> interface (e.g. JdbcMetadataStore)
|
||||
and configure it as a bean in the Application Context.
|
||||
</para>
|
||||
<section id="idempotent-receiver">
|
||||
<title>Idempotent Receiver</title>
|
||||
<para>
|
||||
The <emphasis>Metadata Store</emphasis> is useful for implementating the
|
||||
EIP <ulink url="http://eaipatterns.com/IdempotentReceiver.html">Idempotent Receiver</ulink> pattern, when
|
||||
there is need to <emphasis>filter</emphasis> an incoming Message if it has already been processed, and just discard
|
||||
it or perform some other logic on discarding. The following configuration is an example of how to do this:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int:filter input-channel="serviceChannel"
|
||||
output-channel="idempotentServiceChannel"
|
||||
discard-channel="discardChannel"
|
||||
expression="@metadataStore.get(headers.businessKey) == null"/>
|
||||
|
||||
<int:publish-subscribe-channel id="idempotentServiceChannel"/>
|
||||
|
||||
<int:outbound-channel-adapter channel="idempotentServiceChannel"
|
||||
expression="@metadataStore.put(headers.businessKey, '')"/>
|
||||
|
||||
<int:service-activator input-channel="idempotentServiceChannel" ref="service"/>]]></programlisting>
|
||||
<para>
|
||||
The <code>value</code> of the idempotent entry may be some expiration date, after which that entry should
|
||||
be removed from <emphasis>Metadata Store</emphasis> by some scheduled reaper.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
@@ -424,15 +424,18 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
<title>Redis Metadata Store</title>
|
||||
<para>
|
||||
As of <emphasis>Spring Integration 3.0</emphasis> a new Redis-based
|
||||
<interfacename><ulink url="http://docs.spring.io/spring-integration/docs/latest-ga/api/org/springframework/integration/store/MetadataStore.html">MetadataStore</ulink></interfacename>
|
||||
implementation is available. The <classname>RedisMetadataStore</classname> can
|
||||
<interfacename><ulink url="http://docs.spring.io/spring-integration/docs/latest-ga/api/org/springframework/integration/metadata/MetadataStore.html">MetadataStore</ulink></interfacename>
|
||||
(<xref linkend="metadata-store"/>) implementation is available. The <classname>RedisMetadataStore</classname> can
|
||||
be used to maintain state of a <interfacename>MetadataStore</interfacename>
|
||||
across application restarts. This new <interfacename>MetadataStore</interfacename>
|
||||
implementation can be used with adapters such as:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>Twitter Inbound Adapters</listitem>
|
||||
<listitem>Feed Inbound Channel Adapter</listitem>
|
||||
<listitem><xref linkend="twitter-inbound"/></listitem>
|
||||
<listitem><xref linkend="feed-inbound-channel-adapter"/></listitem>
|
||||
<listitem><xref linkend="file-reading"/></listitem>
|
||||
<listitem><xref linkend="ftp-inbound"/></listitem>
|
||||
<listitem><xref linkend="sftp-inbound"/></listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
In order to instruct these adapters to use the new <classname>RedisMetadataStore</classname>
|
||||
@@ -444,11 +447,16 @@ rt.setConnectionFactory(redisConnectionFactory);]]></programlisting>
|
||||
<programlisting language="xml"><![CDATA[<bean name="metadataStore" class="o.s.i.redis.store.metadata.RedisMetadataStore">
|
||||
<constructor-arg name="connectionFactory" ref="redisConnectionFactory"/>
|
||||
</bean>]]></programlisting>
|
||||
<warning>
|
||||
Be careful when using the same Redis instancce across multiple application
|
||||
contexts as separate adapters may accidentally use the same persisted
|
||||
key.
|
||||
</warning>
|
||||
<para>
|
||||
The <classname>RedisMetadataStore</classname> is backed by
|
||||
<ulink url="http://docs.spring.io/spring-data/data-redis/docs/current/api/org/springframework/data/redis/support/collections/RedisProperties.html"
|
||||
><classname>RedisProperties</classname></ulink> and interaction with it uses
|
||||
<ulink url="http://docs.spring.io/spring-data/data-redis/docs/current/api/org/springframework/data/redis/core/BoundHashOperations.html"
|
||||
><classname>BoundHashOperations</classname></ulink>, which, in turn, requires a <code>key</code> for the entire
|
||||
<classname>Properties</classname> store. In the case of the <interfacename>MetadataStore</interfacename>, this
|
||||
<code>key</code> plays the role of a <emphasis>region</emphasis>, which is useful in distributed environment,
|
||||
when several applications use the same Redis server. By default this <code>key</code> has the value <code>MetaData</code>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="redis-store-inbound-channel-adapter">
|
||||
<title>RedisStore Inbound Channel Adapter</title>
|
||||
|
||||
@@ -317,10 +317,10 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
|
||||
The <classname>AcceptOnceFileListFilter</classname> stores its state in memory. If you wish the
|
||||
state to survive a system restart, consider using the
|
||||
<classname>SftpPersistentAcceptOnceFileListFilter</classname> instead. This filter stores
|
||||
the accepted file names in a <interfacename>MetadataStore</interfacename>. The framework supplies
|
||||
several store implementations (such as Redis), or you can provide your own. This filter matches on
|
||||
the filename and the remote modified time.
|
||||
</para>
|
||||
the accepted file names in an instance of the
|
||||
<interfacename>MetadataStore</interfacename> strategy (<xref linkend="metadata-store"/>).
|
||||
This filter matches on the filename and the remote modified time.
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
Beginning with <emphasis>version 3.0</emphasis>, you can also specify a filter used to filter the files locally, once they have
|
||||
@@ -333,8 +333,8 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
|
||||
The <classname>AcceptOnceFileListFilter</classname> stores its state in memory. If you wish the
|
||||
state to survive a system restart, consider using the
|
||||
<classname>FileSystemPersistentAcceptOnceFileListFilter</classname> as a local filter instead. This filter stores
|
||||
the accepted file names in a <interfacename>MetadataStore</interfacename>. The framework supplies
|
||||
several store implementations (such as Redis), or you can provide your own.
|
||||
the accepted file names in an instance of the
|
||||
<interfacename>MetadataStore</interfacename> strategy (<xref linkend="metadata-store"/>).
|
||||
<important>
|
||||
This filter compares the filename and modified timestamp. If you wish to use this technique to avoid a
|
||||
re-synchronized file from being processed, you should use the <code>preserve-timestamp</code> attribute discussed above.
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>System Management</title>
|
||||
|
||||
|
||||
<xi:include href="./jmx.xml"/>
|
||||
<xi:include href="./message-history.xml"/>
|
||||
<xi:include href="./message-store.xml"/>
|
||||
<xi:include href="./meta-data-store.xml"/>
|
||||
<xi:include href="./control-bus.xml"/>
|
||||
<xi:include href="./shutdown.xml"/>
|
||||
|
||||
|
||||
@@ -148,34 +148,8 @@ twitter.oauth.accessTokenSecret=AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o]]></programlis
|
||||
criteria you'll end up with the same set of tweets unless some other new tweet that matches your search criteria was posted
|
||||
in between your searches. In that situation you'll get all the tweets you had before plus the new one. But what you really
|
||||
want is only the new tweet(s). Spring Integration provides an elegant mechanism for handling these situations.
|
||||
The latest Tweet id will be stored in an instance of the <classname>org.springframework.integration.metadata.MetadataStore</classname> which is a
|
||||
strategy interface designed for storing various types of metadata (e.g., last retrieved tweet in this case). That strategy helps components such as
|
||||
these Twitter adapters avoid duplicates. By default, Spring Integration will look for a bean of type
|
||||
<classname>org.springframework.integration.metadata.MetadataStore</classname> in the ApplicationContext. Alternatively,
|
||||
you can configure an explicit <classname>MetadataStore</classname> on the adapter.
|
||||
If there is no explicit or default store, the adapter will create a new instance of <classname>SimpleMetadataStore</classname>
|
||||
which is a simple in-memory implementation that will only persist metadata within the lifecycle of the currently running application context.
|
||||
That means upon restart you may end up with duplicate entries. If you need to persist metadata between Application Context
|
||||
restarts, you may use the <classname>PropertiesPersistingMetadataStore</classname> (which is backed by a properties file, and a persister
|
||||
strategy), or you may create your own custom implementation of the <classname>MetadataStore</classname> interface (e.g., JdbcMetadatStore)
|
||||
and configure it as a bean named 'metadataStore' within the Application Context.
|
||||
</para>
|
||||
<para>
|
||||
As of <emphasis>Spring Integration 3.0</emphasis> a Redis-based
|
||||
<interfacename>MetadataStore</interfacename> is available. The
|
||||
<classname>RedisMetadataStore</classname> allows you to maintain persisted
|
||||
metadata across Application Context restarts. For more information see <xref linkend="redis-metadata-store" />.
|
||||
</para>
|
||||
<warning>
|
||||
Be careful when using the same Redis instance across multiple application
|
||||
contexts as separate Twitter adapters may accidentally use the same persisted
|
||||
key.
|
||||
</warning>
|
||||
<programlisting language="xml"><![CDATA[<bean id="metadataStore" class="o.s.i.store.PropertiesPersistingMetadataStore"/>
|
||||
]]></programlisting>
|
||||
<para>
|
||||
If the <classname>MetadataStore</classname> is persistent, during initialization, any Inbound Twitter Adapter (see below)
|
||||
will retrieve the latest tweet id that has already been sent by the adapter.
|
||||
The latest Tweet id will be stored in an instance of the <classname>org.springframework.integration.metadata.MetadataStore</classname>
|
||||
strategy (e.g. last retrieved tweet in this case). For more information see <xref linkend="metadata-store"/>.
|
||||
</para>
|
||||
<note>
|
||||
The key used to persist the latest <emphasis>twitter id</emphasis> is the value of the (required)
|
||||
@@ -232,7 +206,7 @@ twitter.oauth.accessTokenSecret=AbRxUAvyNCtqQtxFK8w5ZMtMj20KFhB6o]]></programlis
|
||||
|
||||
</para>
|
||||
<para>
|
||||
Here is a link that will help you learn more about Twitter queries: http://search.twitter.com/operators
|
||||
Refer to <ulink url="https://dev.twitter.com/docs/using-search" /> to learn more about Twitter queries.
|
||||
</para>
|
||||
</section>
|
||||
<para>
|
||||
|
||||
@@ -5,104 +5,25 @@
|
||||
<title>What's new in Spring Integration 3.0?</title>
|
||||
<para>
|
||||
This chapter provides an overview of the new features and improvements
|
||||
that have been introduced with Spring Integration 3.0 If you are interested
|
||||
in even more detail, please take a look at the Issue Tracker tickets that
|
||||
that have been introduced with Spring Integration 3.0. If you are interested
|
||||
in more details, please see the Issue Tracker tickets that
|
||||
were resolved as part of the 3.0 development process.
|
||||
</para>
|
||||
|
||||
<section id="3.0-new-components">
|
||||
<title>New Components</title>
|
||||
<section id="3.0-tcp-events">
|
||||
<title>TCP/IP Connection Events and Connection Management</title>
|
||||
<section id="3.0-request-mapping">
|
||||
<title>HTTP Request Mapping</title>
|
||||
<para>
|
||||
The (supplied) <classname>TcpConnection</classname>s now emit
|
||||
<interfacename>ApplicationEvent</interfacename>s (specifically
|
||||
<classname>TcpConnectionEvent</classname>s) when connections are
|
||||
opened, closed, or an exception occurs. This allows applications
|
||||
to be informed of changes to TCP connections using the normal
|
||||
Spring <interfacename>ApplicationListener</interfacename>
|
||||
mechanism.
|
||||
</para>
|
||||
<para>
|
||||
<classname>AbstractTcpConnection</classname> has been renamed
|
||||
<classname>TcpConnectionSupport</classname>; custom connections that
|
||||
are subclasses of this class, can use its methods to publish events.
|
||||
Similarly, <classname>AbstractTcpConnectionInterceptor</classname> has
|
||||
been renamed to <classname>TcpConnectionInterceptorSupport</classname>.
|
||||
</para>
|
||||
<para>
|
||||
In addition, a new <code><int-ip:tcp-connection-event-inbound-channel-adapter/></code>
|
||||
is provided; by default, this adapter sends all <classname>TcpConnectionEvent</classname>s
|
||||
to a <interfacename>Channel</interfacename>.
|
||||
</para>
|
||||
<para>
|
||||
Further, the TCP Connection Factories, now provide a new method
|
||||
<code>getOpenConnectionIds()</code>, which returns a list of identifiers for all
|
||||
open connections; this allows applications, for example, to broadcast to all
|
||||
open connections.
|
||||
</para>
|
||||
<para>
|
||||
Finally, the connection factories also provide a new method
|
||||
<code>closeConnection(String connectionId)</code> which allows applications
|
||||
to explicitly close a connection using its ID.
|
||||
</para>
|
||||
<para>
|
||||
For more information see <xref linkend="tcp-events"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-syslog">
|
||||
<title>Syslog Support</title>
|
||||
<para>
|
||||
Building on the 2.2 <classname>SyslogToMapTransformer</classname> Spring
|
||||
Integration 3.0 now introduces
|
||||
<code>UDP</code> and <code>TCP</code> inbound channel adapters especially tailored
|
||||
for receiving SYSLOG messages. For more information, see
|
||||
<xref linkend="syslog"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-jmx">
|
||||
<title>JMX Support</title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
A new <code><int-jmx:tree-polling-channel-adapter/></code> is provided; this
|
||||
adapter queries the JMX MBean tree and sends a message with a payload that is the
|
||||
graph of objects that matches the query. By default the MBeans are mapped to
|
||||
primitives and simple Objects like Map, List and arrays - permitting simple
|
||||
transformation, for example, to JSON.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The <classname>IntegrationMBeanExporter</classname> now allows the configuration of
|
||||
a custom <classname>ObjectNamingStrategy</classname> using the <code>naming-strategy</code>
|
||||
attribute.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
For more information, see <xref linkend="jmx"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-tail">
|
||||
<title>'Tail' Support</title>
|
||||
<para>
|
||||
File 'tail'ing inbound channel adapters are now provided to generate messages when
|
||||
lines are added to the end of text files; see <xref linkend="file-tailing"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-inbound-script">
|
||||
<title>Inbound Channel Adapter Script Support</title>
|
||||
<para>
|
||||
The <code><int:inbound-channel-adapter/></code> now supports <code><expression/></code>
|
||||
and <code><script/></code> sub-elements to create a
|
||||
<interfacename>MessageSource</interfacename>; see <xref linkend="channel-adapter-expressions-and-scripts"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-content-enricher-headers">
|
||||
<title>Content Enricher: Headers Enrichment Support</title>
|
||||
<para>
|
||||
The Content Enricher now provides configuration for <code><header/></code>
|
||||
sub-elements, to enrich the outbound Message with headers based on the reply Message from the underlying
|
||||
message flow. For more information see <xref linkend="payload-enricher"/>.
|
||||
The HTTP module now provides powerful Request Mapping support for Inbound Endpoints. Class <classname>UriPathHandlerMapping</classname>
|
||||
was replaced by <classname>IntegrationRequestMappingHandlerMapping</classname>, which is registered under the bean name
|
||||
<code>integrationRequestMappingHandlerMapping</code> in the application context. Upon parsing of the HTTP Inbound Endpoint,
|
||||
a new <classname>IntegrationRequestMappingHandlerMapping</classname> bean is either registered or an existing bean is being reused.
|
||||
To achieve flexible Request Mapping configuration, Spring Integration provides the <code><request-mapping/></code>
|
||||
sub-element for the <code><http:inbound-channel-adapter/></code> and the <code><http:inbound-gateway/></code>.
|
||||
Both HTTP Inbound Endpoints are now fully based on the Request Mapping infrastructure that was introduced with Spring MVC 3.1.
|
||||
For example, multiple paths are supported on a single inbound endpoint.
|
||||
For more information see <xref linkend="http-namespace"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-spel-customization">
|
||||
@@ -131,20 +52,6 @@
|
||||
For more information see <xref linkend="spel-property-accessors" />.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-request-mapping">
|
||||
<title>HTTP Request Mapping</title>
|
||||
<para>
|
||||
The HTTP module now provides powerful Request Mapping support for Inbound Endpoints. Class <classname>UriPathHandlerMapping</classname>
|
||||
was replaced by <classname>IntegrationRequestMappingHandlerMapping</classname>, which is registered under the bean name
|
||||
<code>integrationRequestMappingHandlerMapping</code> in the application context. Upon parsing of the HTTP Inbound Endpoint,
|
||||
a new <classname>IntegrationRequestMappingHandlerMapping</classname> bean is either registered or an existing bean is being reused.
|
||||
To achieve flexible Request Mapping configuration, Spring Integration provides the <code><request-mapping/></code>
|
||||
sub-element for <code><http:inbound-channel-adapter/></code> and <code><http:inbound-gateway/></code>.
|
||||
Both HTTP Inbound Endpoints are now fully based on the Request Mapping infrastructure that was introduced with Spring MVC 3.1.
|
||||
For example, multiple paths are supported on a single inbound endpoint.
|
||||
For more information see <xref linkend="http-namespace"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-redis-new-components">
|
||||
<title>Redis: New Components</title>
|
||||
<para>
|
||||
@@ -191,11 +98,113 @@
|
||||
See <xref linkend="mongodb"/> for more information.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-syslog">
|
||||
<title>Syslog Support</title>
|
||||
<para>
|
||||
Building on the 2.2 <classname>SyslogToMapTransformer</classname> Spring
|
||||
Integration 3.0 now introduces
|
||||
<code>UDP</code> and <code>TCP</code> inbound channel adapters especially tailored
|
||||
for receiving SYSLOG messages. For more information, see
|
||||
<xref linkend="syslog"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-tail">
|
||||
<title>'Tail' Support</title>
|
||||
<para>
|
||||
File 'tail'ing inbound channel adapters are now provided to generate messages when
|
||||
lines are added to the end of text files; see <xref linkend="file-tailing"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-jmx">
|
||||
<title>JMX Support</title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
A new <code><int-jmx:tree-polling-channel-adapter/></code> is provided; this
|
||||
adapter queries the JMX MBean tree and sends a message with a payload that is the
|
||||
graph of objects that matches the query. By default the MBeans are mapped to
|
||||
primitives and simple Objects like Map, List and arrays - permitting simple
|
||||
transformation, for example, to JSON.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The <classname>IntegrationMBeanExporter</classname> now allows the configuration of
|
||||
a custom <classname>ObjectNamingStrategy</classname> using the <code>naming-strategy</code>
|
||||
attribute.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
For more information, see <xref linkend="jmx"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-tcp-events">
|
||||
<title>TCP/IP Connection Events and Connection Management</title>
|
||||
<para>
|
||||
<classname>TcpConnection</classname>s now emit
|
||||
<interfacename>ApplicationEvent</interfacename>s (specifically
|
||||
<classname>TcpConnectionEvent</classname>s) when connections are
|
||||
opened, closed, or an exception occurs. This allows applications
|
||||
to be informed of changes to TCP connections using the normal
|
||||
Spring <interfacename>ApplicationListener</interfacename>
|
||||
mechanism.
|
||||
</para>
|
||||
<para>
|
||||
<classname>AbstractTcpConnection</classname> has been renamed
|
||||
<classname>TcpConnectionSupport</classname>; custom connections that
|
||||
are subclasses of this class, can use its methods to publish events.
|
||||
Similarly, <classname>AbstractTcpConnectionInterceptor</classname> has
|
||||
been renamed to <classname>TcpConnectionInterceptorSupport</classname>.
|
||||
</para>
|
||||
<para>
|
||||
In addition, a new <code><int-ip:tcp-connection-event-inbound-channel-adapter/></code>
|
||||
is provided; by default, this adapter sends all <classname>TcpConnectionEvent</classname>s
|
||||
to a <interfacename>Channel</interfacename>.
|
||||
</para>
|
||||
<para>
|
||||
Further, the TCP Connection Factories, now provide a new method
|
||||
<code>getOpenConnectionIds()</code>, which returns a list of identifiers for all
|
||||
open connections; this allows applications, for example, to broadcast to all
|
||||
open connections.
|
||||
</para>
|
||||
<para>
|
||||
Finally, the connection factories also provide a new method
|
||||
<code>closeConnection(String connectionId)</code> which allows applications
|
||||
to explicitly close a connection using its ID.
|
||||
</para>
|
||||
<para>
|
||||
For more information see <xref linkend="tcp-events"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-inbound-script">
|
||||
<title>Inbound Channel Adapter Script Support</title>
|
||||
<para>
|
||||
The <code><int:inbound-channel-adapter/></code> now supports <code><expression/></code>
|
||||
and <code><script/></code> sub-elements to create a
|
||||
<interfacename>MessageSource</interfacename>; see <xref linkend="channel-adapter-expressions-and-scripts"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-content-enricher-headers">
|
||||
<title>Content Enricher: Headers Enrichment Support</title>
|
||||
<para>
|
||||
The Content Enricher now provides configuration for <code><header/></code>
|
||||
sub-elements, to enrich the outbound Message with headers based on the reply Message from the underlying
|
||||
message flow. For more information see <xref linkend="payload-enricher"/>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="3.0-general">
|
||||
<title>General Changes</title>
|
||||
|
||||
<section id="3.0-message-id">
|
||||
<title>Message ID Generation</title>
|
||||
<para>
|
||||
Previously, message ids were generated using the JDK <code>UUID.randomUUID()</code> method. With this
|
||||
release, the default mechanism has been changed to use a more efficient algorithm which
|
||||
is significantly faster. In addition, the ability to change
|
||||
the strategy used to generate message ids has been added.
|
||||
For more information see <xref linkend="message-id-generation"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-gateway">
|
||||
<title><gateway> Changes</title>
|
||||
<para>
|
||||
@@ -208,204 +217,16 @@
|
||||
It is now possible to entirely customize the way that gateway method calls are mapped
|
||||
to messages.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The <classname>GatewayMethodMetadata</classname> is now public class and it makes possible flexibly
|
||||
to configure the <classname>GatewayProxyFactoryBean</classname> programmatically from Java code.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
For more information see <xref linkend="gateway"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-corr-endpoint-empty-groups">
|
||||
<title>Aggregator 'empty-group-min-timeout' property</title>
|
||||
<para><classname>AbstractCorrelatingMessageHandler</classname> provides a new property
|
||||
<code>empty-group-min-timeout</code>
|
||||
to allow empty group expiry to run on a longer schedule than expiring partial groups. Empty groups will
|
||||
not be removed from the <interfacename>MessageStore</interfacename> until they have not been modified
|
||||
for at least this number of milliseconds. For more information see <xref linkend="aggregator-config"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-advising-filters">
|
||||
<title>Advising Filters</title>
|
||||
<para>
|
||||
Previously, when a <filter/> had a <request-handler-advice-chain/>, the discard
|
||||
action was all performed within the scope of the advice chain (including any downstream flow
|
||||
on the <code>discard-channel</code>). The filter element now has an attribute
|
||||
<code>discard-within-advice</code> (default <code>true</code>), to allow the discard action to
|
||||
be performed after the advice chain completes. See <xref linkend="advising-filters"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-annotation-advice">
|
||||
<title>Advising Endpoints using Annotations</title>
|
||||
<para>
|
||||
Request Handler Advice Chains can now be configured using annotations. See
|
||||
<xref linkend="advising-with-annotations"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-o-t-s-t">
|
||||
<title>ObjectToStringTransformer Improvements</title>
|
||||
<para>
|
||||
This transformer now correctly transforms <code>byte[]</code> and <code>char[]</code>
|
||||
payloads to <classname>String</classname>. For more information see <xref linkend="transformer"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-ws-outbound-uri-substitution">
|
||||
<title>Web Service Outbound URI Configuration</title>
|
||||
<para>
|
||||
Web Service Outbound Gateway 'uri' attribute now supports <code><uri-variable/></code> substitution for all
|
||||
URI-schemes supported by Spring Web Services. For more information see <xref linkend="outbound-uri"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-ftp-cache-changes">
|
||||
<title>FTP, SFTP and FTPS Cached Sessions</title>
|
||||
<para>
|
||||
The FTP, SFTP and FTPS endpoints no longer cache sessions by default.
|
||||
</para>
|
||||
<para>
|
||||
The deprecated <code>cached-sessions</code> attribute has been removed from all endpoints.
|
||||
Previously, the embedded caching mechanism controlled by this attribute's value didn't
|
||||
provide a way to limit the size of the cache, which could
|
||||
grow indefinitely. The <classname>CachingConnectionFactory</classname> was introduced in
|
||||
release 2.1 and it became the preferred (and is now the only) way to cache sessions.
|
||||
For more information, see
|
||||
<xref linkend="ftp-session-caching"/> and <xref linkend="sftp-session-caching"/>.
|
||||
</para>
|
||||
<para>
|
||||
The <classname>CachingConnectionFactory</classname> now provides a new method
|
||||
<code>resetCache()</code>. This immediately closes idle sessions and causes in-use
|
||||
sessions to be closed as and when they are returned to the cache.
|
||||
</para>
|
||||
<para>
|
||||
The <classname>DefaultSftpSessionFactory</classname> (in conjunction with a
|
||||
<classname>CachingSessionFactory</classname>) now supports multiplexing channels over
|
||||
a single SSH connection (SFTP Only).
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-xFTP-ib">
|
||||
<title>FTP, SFTP and FTPS Inbound Adapters</title>
|
||||
<para>
|
||||
Previously, there was no way to override the default filter used to process files retrieved
|
||||
from a remote server. The <code>filter</code> attribute determines which files are retrieved
|
||||
but the <classname>FileReadingMessageSource</classname> uses an
|
||||
<classname>AcceptOnceFileListFilter</classname>. This means that if a new copy of a file
|
||||
is retrieved, with the same name as a previously copied file, no message was sent from the
|
||||
adapter.
|
||||
</para>
|
||||
<para>
|
||||
With this release, a new attribute <code>local-filter</code> allows you to override the
|
||||
default filter, for example with an <classname>AcceptAllFileListFilter</classname>, or some
|
||||
other custom filter.
|
||||
</para>
|
||||
<para>
|
||||
For users that wish the behavior of the <classname>AcceptOnceFileListFilter</classname>
|
||||
to be maintained across JVM executions, a custom filter that retains state, perhaps on
|
||||
the file system, can now be configured.
|
||||
</para>
|
||||
<para>
|
||||
Inbound Channel Adapters now support the <code>preserve-timestamp</code> attribute, which
|
||||
sets the local file modified timestamp to the timestamp from the server (default false).
|
||||
</para>
|
||||
<para>
|
||||
For more information, see
|
||||
<xref linkend="ftp-inbound"/> and <xref linkend="sftp-inbound"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-xFTP-gw">
|
||||
<title>FTP, SFTP and FTPS Gateways</title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
The gateways now support the <emphasis role="bold">mv</emphasis> command, enabling
|
||||
the renaming of remote files.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The gateways now support recursive <emphasis role="bold">ls</emphasis> and
|
||||
<emphasis role="bold">mget</emphasis> commands, enabling
|
||||
the retrieval of a remote file tree.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The gateways now support <emphasis role="bold">put</emphasis> and
|
||||
<emphasis role="bold">mput</emphasis> commands, enabling
|
||||
sending file(s) to the remote server.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The <code>local-filename-generator-expression</code> attribute is now supported,
|
||||
enabling the naming of local files during retrieval. By default, the same
|
||||
name as the remote file is used.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The <code>local-directory-expression</code> attribute is now supported,
|
||||
enabling the naming of local directories during retrieval based on the remote directory.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
For more information, see <xref linkend="ftp-outbound-gateway"/> and <xref linkend="sftp-outbound-gateway"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-remote-file-template">
|
||||
<title>Remote File Template</title>
|
||||
<para>
|
||||
A new higher-level abstraction (<classname>RemoteFileTemplate</classname>) is provided over the
|
||||
<interfacename>Session</interfacename> implementations used by the FTP and SFTP modules. While it is
|
||||
used internally by endpoints, this abstraction can also be used programmatically and, like all
|
||||
Spring <code>*Template</code> implemenations, reliably closes the underlying session while allowing
|
||||
low level access to the session when needed.
|
||||
</para>
|
||||
<para>
|
||||
For more information, see
|
||||
<xref linkend="ftp-rft"/> and <xref linkend="sftp-rft"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-jdbc-mysql-v5_6_4">
|
||||
<title>JDBC Message Store Improvements</title>
|
||||
<para>
|
||||
<emphasis>Spring Integration 3.0</emphasis> adds a new set of DDL
|
||||
scripts for <emphasis>MySQL</emphasis> version 5.6.4 and higher.
|
||||
Now <emphasis>MySQL</emphasis> supports <emphasis>fractional
|
||||
seconds</emphasis> and is thus improving the FIFO ordering when
|
||||
polling from a MySQL-based Message Store. For more information,
|
||||
please see <xref linkend="jdbc-message-store-generic"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-jpa-persist-merge-collections">
|
||||
<title>JPA Support: Improvements</title>
|
||||
<para>
|
||||
Payloads to <emphasis>persist</emphasis> or
|
||||
<emphasis>merge</emphasis> can now be of type
|
||||
<interfacename><ulink url="http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html"
|
||||
>java.lang.Iterable</ulink></interfacename>.
|
||||
</para>
|
||||
<para>
|
||||
In that case, each object returned by the
|
||||
<interfacename>Iterable</interfacename> is treated as
|
||||
an entity and persisted or merged using the underlying
|
||||
<interfacename>EntityManager</interfacename>.
|
||||
<emphasis>NULL</emphasis> values returned by the iterator are ignored.
|
||||
</para>
|
||||
<para>
|
||||
The JPA adapters now have additional attributes to optionally 'flush' and 'clear'
|
||||
entities from the associated persistence context after performing persistence operations.
|
||||
</para>
|
||||
<para>For more information see <xref linkend="jpa"/>.</para>
|
||||
</section>
|
||||
<section id="3.0-json-transformers">
|
||||
<title>Jackson Support (JSON)</title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
A new abstraction for JSON conversion has been introduced. Implementations for Jackson 1.x
|
||||
and Jackson 2 are currently provided, with the version being determined by presence on
|
||||
the classpath. Previously, only Jackson 1.x was supported.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The <classname>ObjectToJsonTransformer</classname> and <classname>JsonToObjectTransformer</classname>
|
||||
now emit/consume headers containing type information.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
For more information, see 'JSON Transformers' in <xref linkend="transformer"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-http-endpointss">
|
||||
<title>HTTP Endpoint Changes</title>
|
||||
<para>
|
||||
@@ -450,22 +271,23 @@
|
||||
For more information see <xref linkend="http"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-amqp-mapping">
|
||||
<title>AMQP Outbound Gateway Header Mapping</title>
|
||||
<section id="3.0-json-transformers">
|
||||
<title>Jackson Support (JSON)</title>
|
||||
<para>
|
||||
Previously, the <int-amqp:outbound-gateway/> mapped headers before invoking the message
|
||||
converter, and the converter could overwrite headers such as <code>content-type</code>. The
|
||||
outbound adapter maps the headers after the conversion, which means headers like
|
||||
<code>content-type</code> from the outbound <code>Message</code> (if present) are used.
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
A new abstraction for JSON conversion has been introduced. Implementations for Jackson 1.x
|
||||
and Jackson 2 are currently provided, with the version being determined by presence on
|
||||
the classpath. Previously, only Jackson 1.x was supported.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The <classname>ObjectToJsonTransformer</classname> and <classname>JsonToObjectTransformer</classname>
|
||||
now emit/consume headers containing type information.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
Starting with this release, the gateway now maps the headers after the message conversion,
|
||||
consistent with the adapter. If your application relies on the previous behavior (where the
|
||||
converter's headers overrode the mapped headers), you either need to filter those headers
|
||||
(before the message reaches the gateway)
|
||||
or set them appropriately. The headers affected by the <classname>SimpleMessageConverter</classname>
|
||||
are <code>content-type</code> and <code>content-encoding</code>. Custom message converters
|
||||
may set other headers.
|
||||
For more information, see 'JSON Transformers' in <xref linkend="transformer"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-id-for-chain-sub-components">
|
||||
@@ -479,85 +301,145 @@
|
||||
For more information see <xref linkend="chain"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-jms-mdca-te">
|
||||
<title>JMS Message Driven Channel Adapter</title>
|
||||
<section id="3.0-corr-endpoint-empty-groups">
|
||||
<title>Aggregator 'empty-group-min-timeout' property</title>
|
||||
<para>
|
||||
Previously, when configuring a <code><message-driven-channel-adapter/></code>, if you wished to
|
||||
use a specific <interfacename>TaskExecutor</interfacename>, it was necessary to declare a container
|
||||
bean and provide it to the adapter using the <code>container</code> attribute. The
|
||||
<code>task-executor</code> is now provided, allowing it to be set directly on the adapter. This is
|
||||
in addition to several other container attributes that were already available.
|
||||
The <classname>AbstractCorrelatingMessageHandler</classname> provides a new property
|
||||
<code>empty-group-min-timeout</code>
|
||||
to allow empty group expiry to run on a longer schedule than expiring partial groups. Empty groups will
|
||||
not be removed from the <interfacename>MessageStore</interfacename> until they have not been modified
|
||||
for at least this number of milliseconds. For more information see <xref linkend="aggregator-config"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-rmi-ec">
|
||||
<title>RMI Inbound Gateway</title>
|
||||
<section id="3.0-filelistfilter">
|
||||
<title>Persistent File List Filters (file, (S)FTP)</title>
|
||||
<para>
|
||||
The RMI Inbound Gateway now supports an <code>error-channel</code> attribute. See
|
||||
<xref linkend="rmi-inbound"/>.
|
||||
New <classname>FileListFilter</classname>s that use a persistent <classname>MetadataStore</classname> are
|
||||
now available. These can be used to prevent duplicate files after a system restart. See
|
||||
<xref linkend="file-reading"/>, <xref linkend="ftp-inbound"/>, and <xref linkend="sftp-inbound"/> for more information.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-stored-proc-sql-return-type">
|
||||
<title>Stored Procedure Components Improvements</title>
|
||||
<section id="3.0-scripting-variables">
|
||||
<title>Scripting Support: Variables Changes</title>
|
||||
<para>
|
||||
For more complex database-specific types, not supported by the standard
|
||||
<code>CallableStatement.getObject</code> method, 2 new additional
|
||||
attributes were introduced to the <code><sql-parameter-definition/></code>
|
||||
element with OUT-direction:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem><emphasis>type-name</emphasis></listitem>
|
||||
<listitem><emphasis>return-type</emphasis></listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
<para>
|
||||
The <code>row-mapper</code> attribute of the Stored Procedure Inbound Channel Adapter
|
||||
<code><returning-resultset/></code> sub-element
|
||||
now supports a reference to a <interfacename>RowMapper</interfacename> bean
|
||||
definition. Previously, it contained just a class name (which is still supported).
|
||||
</para>
|
||||
<para>
|
||||
For more information see <xref linkend="stored-procedures"/>.
|
||||
</para>
|
||||
A new <code>variables</code> attribute has been introduced for scripting components.
|
||||
In addition, variable bindings are now allowed for inline scripts.
|
||||
See <xref linkend="groovy"/> and <xref linkend="scripting"/> for more information.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-event-for-imap-idle">
|
||||
<title>IMAP Idle Connection Exceptions</title>
|
||||
<section id="3.0-direct-channel-lb-ref">
|
||||
<title>Direct Channel Load Balancing configuration</title>
|
||||
<para>
|
||||
Previously, if an IMAP idle connection failed, it was logged but there was no mechanism to
|
||||
inform an application. Such exceptions now generate <classname>ApplicationEvent</classname>s.
|
||||
Applications can obtain these events using an <code><int-event:inbound-channel-adapter></code>
|
||||
or any <interfacename>ApplicationListener</interfacename> configured to receive an
|
||||
<classname>ImapIdleExceptionEvent</classname> or one of its super classes.
|
||||
Previously, when configuring <classname>LoadBalancingStrategy</classname> on the channel's 'dispatcher' sub-element,
|
||||
the only available option was to use a pre-defined enumeration of values which did not allow one to set a custom implementation
|
||||
of the <classname>LoadBalancingStrategy</classname>. You can now use <code>load-balancer-ref</code> to provide
|
||||
a reference to a custom implementation of the <classname>LoadBalancingStrategy</classname>.
|
||||
For more information see <xref linkend="channel-implementations-directchannel"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-message-id">
|
||||
<title>Message ID Generation</title>
|
||||
<section id="3.0-pub-sub">
|
||||
<title>PublishSubscribeChannel Behavior</title>
|
||||
<para>
|
||||
Previously, message ids were generated using the JDK <code>UUID.randomUUID()</code> method. With this
|
||||
release, the default mechanism has been changed to use a more efficient algorithm which
|
||||
is significantly faster. In addition, the ability to change
|
||||
the strategy used to generate message ids has been added.
|
||||
For more information see <xref linkend="message-id-generation"/>.
|
||||
Previously, sending to a <publish-subscribe-channel/> that had
|
||||
no subscribers would return a <code>false</code> result. If used in conjunction with
|
||||
a <classname>MessagingTemplate</classname>, this would result in an exception being thrown.
|
||||
Now, the <classname>PublishSubscribeChannel</classname> has a property
|
||||
<code>minSubscribers</code> (default 0). If the message is sent to at least the minimum
|
||||
number of subscribers, the send is deemed to be successful (even if zero). If an application
|
||||
is expecting to get an exception under these conditions, set the minimum subscribers to at
|
||||
least 1.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-xslt-transformer">
|
||||
<title>XsltPayloadTransformer</title>
|
||||
<section id="3.0-(s)ftp-changes">
|
||||
<title>FTP, SFTP and FTPS Changes</title>
|
||||
<para>
|
||||
You can now specify the transformer factory class name using the
|
||||
<code>transformer-factory-class</code> attribute. See <xref linkend="xml-xslt-payload-transformers"/>
|
||||
<emphasis role="bold">The FTP, SFTP and FTPS endpoints no longer cache sessions by default</emphasis>
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-tcp-headers">
|
||||
<title>Message Headers and TCP</title>
|
||||
<para>
|
||||
The TCP connection factories now enable the configuration of a flexible mechanism to
|
||||
transfer selected headers (as well as the payload) over TCP. A new
|
||||
<classname>TcpMessageMapper</classname>
|
||||
enables the selection of the headers, and an appropriate (de)serializer needs to be
|
||||
configured to write the resulting <interfacename>Map</interfacename> to the
|
||||
TCP stream. A <classname>MapJsonSerializer</classname> is provided as a convenient
|
||||
mechanism to transfer headers and payload over TCP.
|
||||
For more information see <xref linkend="ip-headers"/>.
|
||||
The deprecated <code>cached-sessions</code> attribute has been removed from all endpoints.
|
||||
Previously, the embedded caching mechanism controlled by this attribute's value didn't
|
||||
provide a way to limit the size of the cache, which could
|
||||
grow indefinitely. The <classname>CachingConnectionFactory</classname> was introduced in
|
||||
release 2.1 and it became the preferred (and is now the only) way to cache sessions.
|
||||
</para>
|
||||
<para>
|
||||
The <classname>CachingConnectionFactory</classname> now provides a new method
|
||||
<code>resetCache()</code>. This immediately closes idle sessions and causes in-use
|
||||
sessions to be closed as and when they are returned to the cache.
|
||||
</para>
|
||||
<para>
|
||||
The <classname>DefaultSftpSessionFactory</classname> (in conjunction with a
|
||||
<classname>CachingSessionFactory</classname>) now supports multiplexing channels over
|
||||
a single SSH connection (SFTP Only).
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">FTP, SFTP and FTPS Inbound Adapters</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
Previously, there was no way to override the default filter used to process files retrieved
|
||||
from a remote server. The <code>filter</code> attribute determines which files are retrieved
|
||||
but the <classname>FileReadingMessageSource</classname> uses an
|
||||
<classname>AcceptOnceFileListFilter</classname>. This means that if a new copy of a file
|
||||
is retrieved, with the same name as a previously copied file, no message was sent from the
|
||||
adapter.
|
||||
</para>
|
||||
<para>
|
||||
With this release, a new attribute <code>local-filter</code> allows you to override the
|
||||
default filter, for example with an <classname>AcceptAllFileListFilter</classname>, or some
|
||||
other custom filter.
|
||||
</para>
|
||||
<para>
|
||||
For users that wish the behavior of the <classname>AcceptOnceFileListFilter</classname>
|
||||
to be maintained across JVM executions, a custom filter that retains state, perhaps on
|
||||
the file system, can now be configured.
|
||||
</para>
|
||||
<para>
|
||||
Inbound Channel Adapters now support the <code>preserve-timestamp</code> attribute, which
|
||||
sets the local file modified timestamp to the timestamp from the server (default false).
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">FTP, SFTP and FTPS Gateways</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
The gateways now support the <emphasis role="bold">mv</emphasis> command, enabling
|
||||
the renaming of remote files.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The gateways now support recursive <emphasis role="bold">ls</emphasis> and
|
||||
<emphasis role="bold">mget</emphasis> commands, enabling
|
||||
the retrieval of a remote file tree.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The gateways now support <emphasis role="bold">put</emphasis> and
|
||||
<emphasis role="bold">mput</emphasis> commands, enabling
|
||||
sending file(s) to the remote server.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The <code>local-filename-generator-expression</code> attribute is now supported,
|
||||
enabling the naming of local files during retrieval. By default, the same
|
||||
name as the remote file is used.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The <code>local-directory-expression</code> attribute is now supported,
|
||||
enabling the naming of local directories during retrieval based on the remote directory.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">Remote File Template</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
A new higher-level abstraction (<classname>RemoteFileTemplate</classname>) is provided over the
|
||||
<interfacename>Session</interfacename> implementations used by the FTP and SFTP modules. While it is
|
||||
used internally by endpoints, this abstraction can also be used programmatically and, like all
|
||||
Spring <code>*Template</code> implementations, reliably closes the underlying session while allowing
|
||||
low level access to the session when needed.
|
||||
</para>
|
||||
<para>
|
||||
For more information, see
|
||||
<xref linkend="ftp"/> and <xref linkend="sftp"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-outbound-gateway-requires-reply">
|
||||
@@ -593,54 +475,53 @@
|
||||
set <code>requires-reply</code> to false.
|
||||
</important>
|
||||
</section>
|
||||
<section id="3.0-dalay-expression">
|
||||
<title>Delayer: delay expression</title>
|
||||
<section id="3.0-amqp-mapping">
|
||||
<title>AMQP Outbound Gateway Header Mapping</title>
|
||||
<para>
|
||||
Previously, the <code><delayer></code> provided a <code>delay-header-name</code> attribute
|
||||
to determine the <emphasis>delay</emphasis> value at runtime. In complex cases it was necessary
|
||||
to precede the <code><delayer></code> with a <code><header-enricher></code>.
|
||||
Spring Integration 3.0 introduced the <code>expression</code> attribute and <code>expression</code>
|
||||
sub-element for dynamic delay determination. The <code>delay-header-name</code> attribute is now deprecated
|
||||
because the header evaluation can be specified in the <code>expression</code>. In addition,
|
||||
the <code>ignore-expression-failures</code> was introduced to control the behavior when an
|
||||
expression evaluation fails.
|
||||
For more information see <xref linkend="delayer"/>.
|
||||
Previously, the <int-amqp:outbound-gateway/> mapped headers before invoking the message
|
||||
converter, and the converter could overwrite headers such as <code>content-type</code>. The
|
||||
outbound adapter maps the headers after the conversion, which means headers like
|
||||
<code>content-type</code> from the outbound <code>Message</code> (if present) are used.
|
||||
</para>
|
||||
<para>
|
||||
Starting with this release, the gateway now maps the headers after the message conversion,
|
||||
consistent with the adapter. If your application relies on the previous behavior (where the
|
||||
converter's headers overrode the mapped headers), you either need to filter those headers
|
||||
(before the message reaches the gateway)
|
||||
or set them appropriately. The headers affected by the <classname>SimpleMessageConverter</classname>
|
||||
are <code>content-type</code> and <code>content-encoding</code>. Custom message converters
|
||||
may set other headers.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-pub-sub">
|
||||
<title>PublishSubscribeChannel Behavior</title>
|
||||
<section id="3.0-stored-proc-sql-return-type">
|
||||
<title>Stored Procedure Components Improvements</title>
|
||||
<para>
|
||||
Previously, sending to a <publish-subscribe-channel/> that had
|
||||
no subscribers would return a <code>false</code> result. If used in conjunction with
|
||||
a <classname>MessagingTemplate</classname>, this would result in an exception being thrown.
|
||||
Now, the <classname>PublishSubscribeChannel</classname> has a property
|
||||
<code>minSubscribers</code> (default 0). If the message is sent to at least the minimum
|
||||
number of subscribers, the send is deemed to be successful (even if zero). If an application
|
||||
is expecting to get an exception under these conditions, set the minimum subscribers to at
|
||||
least 1.
|
||||
For more complex database-specific types, not supported by the standard
|
||||
<code>CallableStatement.getObject</code> method, 2 new additional
|
||||
attributes were introduced to the <code><sql-parameter-definition/></code>
|
||||
element with OUT-direction:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem><emphasis>type-name</emphasis></listitem>
|
||||
<listitem><emphasis>return-type</emphasis></listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
<para>
|
||||
The <code>row-mapper</code> attribute of the Stored Procedure Inbound Channel Adapter
|
||||
<code><returning-resultset/></code> sub-element
|
||||
now supports a reference to a <interfacename>RowMapper</interfacename> bean
|
||||
definition. Previously, it contained just a class name (which is still supported).
|
||||
</para>
|
||||
<para>
|
||||
For more information see <xref linkend="stored-procedures"/>.
|
||||
</para>
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-jpa-first-result">
|
||||
<title>JPA Adapters: first-result attribute</title>
|
||||
<section id="3.0-ws-outbound-uri-substitution">
|
||||
<title>Web Service Outbound URI Configuration</title>
|
||||
<para>
|
||||
Retrieving gateways had no mechanism to specify the first record to be retrieved which
|
||||
is a common use case. The retrieving gateways now support specifying this parameter
|
||||
using a <code>first-result</code> and <code>first-result-expression</code> attributes
|
||||
to the gateway definition. <xref linkend="jpa-retrieving-outbound-gateway"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-jpa-max-results">
|
||||
<title>JPA Adapters: max-results and max-results-expression Attributes</title>
|
||||
<para>
|
||||
The JPA retrieving gateway and inbound adapter now have an attribute to specify the maximum
|
||||
number of results in a result set as an expression. In addition, the
|
||||
<code>max-results</code> attribute has been introduced to replace
|
||||
<code>max-number-of-results</code>, which has been deprecated.
|
||||
<code>max-results</code> and <code>max-results-expression</code>
|
||||
are used to provide the maximum number of results,
|
||||
or an expression to compute the maximum number of results, respectively, in the
|
||||
result set.
|
||||
For more information see <xref linkend="jpa"/>.
|
||||
Web Service Outbound Gateway 'uri' attribute now supports <code><uri-variable/></code> substitution for all
|
||||
URI-schemes supported by Spring Web Services. For more information see <xref linkend="outbound-uri"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-redis">
|
||||
@@ -665,20 +546,137 @@
|
||||
For more information, see <xref linkend="redis"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-filelistfilter">
|
||||
<title>Persistent File List Filters (file, (S)FTP)</title>
|
||||
<section id="3.0-advising-filters">
|
||||
<title>Advising Filters</title>
|
||||
<para>
|
||||
New <classname>FileListFilter</classname>s that use a persistent <classname>MetadataStore</classname> are
|
||||
now available. These can be used to prevent duplicate files after a system restart. See
|
||||
<xref linkend="file-reading"/>, <xref linkend="ftp-inbound"/>, and <xref linkend="sftp-inbound"/> for more information.
|
||||
Previously, when a <filter/> had a <request-handler-advice-chain/>, the discard
|
||||
action was all performed within the scope of the advice chain (including any downstream flow
|
||||
on the <code>discard-channel</code>). The filter element now has an attribute
|
||||
<code>discard-within-advice</code> (default <code>true</code>), to allow the discard action to
|
||||
be performed after the advice chain completes. See <xref linkend="advising-filters"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-scripting-variables">
|
||||
<title>Scripting Support: Variables Changes</title>
|
||||
<section id="3.0-annotation-advice">
|
||||
<title>Advising Endpoints using Annotations</title>
|
||||
<para>
|
||||
A new <code>variables</code> attribute has been introduced for scripting components.
|
||||
In addition, variable bindings are now allowed for inline scripts.
|
||||
See <xref linkend="groovy"/> and <xref linkend="scripting"/> for more information.
|
||||
Request Handler Advice Chains can now be configured using annotations. See
|
||||
<xref linkend="advising-with-annotations"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-o-t-s-t">
|
||||
<title>ObjectToStringTransformer Improvements</title>
|
||||
<para>
|
||||
This transformer now correctly transforms <code>byte[]</code> and <code>char[]</code>
|
||||
payloads to <classname>String</classname>. For more information see <xref linkend="transformer"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-jpa-changes">
|
||||
<title>JPA Support Changes</title>
|
||||
<para>
|
||||
Payloads to <emphasis>persist</emphasis> or
|
||||
<emphasis>merge</emphasis> can now be of type
|
||||
<interfacename><ulink url="http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html"
|
||||
>java.lang.Iterable</ulink></interfacename>.
|
||||
</para>
|
||||
<para>
|
||||
In that case, each object returned by the
|
||||
<interfacename>Iterable</interfacename> is treated as
|
||||
an entity and persisted or merged using the underlying
|
||||
<interfacename>EntityManager</interfacename>.
|
||||
<emphasis>NULL</emphasis> values returned by the iterator are ignored.
|
||||
</para>
|
||||
<para>
|
||||
The JPA adapters now have additional attributes to optionally 'flush' and 'clear'
|
||||
entities from the associated persistence context after performing persistence operations.
|
||||
</para>
|
||||
<para>
|
||||
Retrieving gateways had no mechanism to specify the first record to be retrieved which
|
||||
is a common use case. The retrieving gateways now support specifying this parameter
|
||||
using a <code>first-result</code> and <code>first-result-expression</code> attributes
|
||||
to the gateway definition. <xref linkend="jpa-retrieving-outbound-gateway"/>.
|
||||
</para>
|
||||
<para>
|
||||
The JPA retrieving gateway and inbound adapter now have an attribute to specify the maximum
|
||||
number of results in a result set as an expression. In addition, the
|
||||
<code>max-results</code> attribute has been introduced to replace
|
||||
<code>max-number-of-results</code>, which has been deprecated.
|
||||
<code>max-results</code> and <code>max-results-expression</code>
|
||||
are used to provide the maximum number of results,
|
||||
or an expression to compute the maximum number of results, respectively, in the
|
||||
result set.
|
||||
</para>
|
||||
<para>For more information see <xref linkend="jpa"/>.</para>
|
||||
</section>
|
||||
<section id="3.0-dalay-expression">
|
||||
<title>Delayer: delay expression</title>
|
||||
<para>
|
||||
Previously, the <code><delayer></code> provided a <code>delay-header-name</code> attribute
|
||||
to determine the <emphasis>delay</emphasis> value at runtime. In complex cases it was necessary
|
||||
to precede the <code><delayer></code> with a <code><header-enricher></code>.
|
||||
Spring Integration 3.0 introduced the <code>expression</code> attribute and <code>expression</code>
|
||||
sub-element for dynamic delay determination. The <code>delay-header-name</code> attribute is now deprecated
|
||||
because the header evaluation can be specified in the <code>expression</code>. In addition,
|
||||
the <code>ignore-expression-failures</code> was introduced to control the behavior when an
|
||||
expression evaluation fails.
|
||||
For more information see <xref linkend="delayer"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-jdbc-mysql-v5_6_4">
|
||||
<title>JDBC Message Store Improvements</title>
|
||||
<para>
|
||||
<emphasis>Spring Integration 3.0</emphasis> adds a new set of DDL
|
||||
scripts for <emphasis>MySQL</emphasis> version 5.6.4 and higher.
|
||||
Now <emphasis>MySQL</emphasis> supports <emphasis>fractional
|
||||
seconds</emphasis> and is thus improving the FIFO ordering when
|
||||
polling from a MySQL-based Message Store. For more information,
|
||||
please see <xref linkend="jdbc-message-store-generic"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-event-for-imap-idle">
|
||||
<title>IMAP Idle Connection Exceptions</title>
|
||||
<para>
|
||||
Previously, if an IMAP idle connection failed, it was logged but there was no mechanism to
|
||||
inform an application. Such exceptions now generate <classname>ApplicationEvent</classname>s.
|
||||
Applications can obtain these events using an <code><int-event:inbound-channel-adapter></code>
|
||||
or any <interfacename>ApplicationListener</interfacename> configured to receive an
|
||||
<classname>ImapIdleExceptionEvent</classname> or one of its super classes.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-tcp-headers">
|
||||
<title>Message Headers and TCP</title>
|
||||
<para>
|
||||
The TCP connection factories now enable the configuration of a flexible mechanism to
|
||||
transfer selected headers (as well as the payload) over TCP. A new
|
||||
<classname>TcpMessageMapper</classname>
|
||||
enables the selection of the headers, and an appropriate (de)serializer needs to be
|
||||
configured to write the resulting <interfacename>Map</interfacename> to the
|
||||
TCP stream. A <classname>MapJsonSerializer</classname> is provided as a convenient
|
||||
mechanism to transfer headers and payload over TCP.
|
||||
For more information see <xref linkend="ip-headers"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-jms-mdca-te">
|
||||
<title>JMS Message Driven Channel Adapter</title>
|
||||
<para>
|
||||
Previously, when configuring a <code><message-driven-channel-adapter/></code>, if you wished to
|
||||
use a specific <interfacename>TaskExecutor</interfacename>, it was necessary to declare a container
|
||||
bean and provide it to the adapter using the <code>container</code> attribute. The
|
||||
<code>task-executor</code> is now provided, allowing it to be set directly on the adapter. This is
|
||||
in addition to several other container attributes that were already available.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-rmi-ec">
|
||||
<title>RMI Inbound Gateway</title>
|
||||
<para>
|
||||
The RMI Inbound Gateway now supports an <code>error-channel</code> attribute. See
|
||||
<xref linkend="rmi-inbound"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-xslt-transformer">
|
||||
<title>XsltPayloadTransformer</title>
|
||||
<para>
|
||||
You can now specify the transformer factory class name using the
|
||||
<code>transformer-factory-class</code> attribute. See <xref linkend="xml-xslt-payload-transformers"/>
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -340,12 +340,8 @@
|
||||
<interfacename><ulink url="http://static.springsource.org/spring-integration/api/org/springframework/integration/transformer/Transformer.html">Transformer</ulink></interfacename>.
|
||||
When configuring XML transformers as beans in Spring Integration,
|
||||
you would normally configure the <emphasis>Transformer</emphasis>
|
||||
in conjunction with either a
|
||||
<classname><ulink url="http://static.springsource.org/spring-integration/api/org/springframework/integration/transformer/MessageTransformingChannelInterceptor.html">MessageTransformingChannelInterceptor</ulink></classname>
|
||||
or a <classname><ulink url="http://static.springsource.org/spring-integration/api/org/springframework/integration/transformer/MessageTransformingHandler.html">MessageTransformingHandler</ulink></classname>.
|
||||
This allows the transformer to be used as either an interceptor,
|
||||
which transforms the message as it is sent or received to the
|
||||
<emphasis>Channel</emphasis>, or as an <emphasis>Endpoint</emphasis>.
|
||||
in conjunction with a <classname><ulink url="http://static.springsource.org/spring-integration/api/org/springframework/integration/transformer/MessageTransformingHandler.html">MessageTransformingHandler</ulink></classname>.
|
||||
This allows the transformer to be used as an <emphasis>Endpoint</emphasis>.
|
||||
Finally, the namespace support will be discussed, which allows for
|
||||
the simple configuration of the transformers as elements in XML.
|
||||
</para>
|
||||
|
||||
Reference in New Issue
Block a user