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:
Gary Russell
2013-12-16 11:02:40 -05:00
77 changed files with 1626 additions and 1685 deletions

View File

@@ -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') {

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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");
}

View File

@@ -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 &lt;gateway&gt; element would look like this:
* &lt;method name="echo" request-channel="inputA" reply-timeout="2" request-timeout="200"/&gt;
*
*
* @author Oleg Zhurakousky
* @author Gary Russell
* @since 2.0
*/
class GatewayMethodMetadata {
public class GatewayMethodMetadata {
private volatile String payloadExpression;

View File

@@ -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

View File

@@ -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;

View File

@@ -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[

View File

@@ -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();
}
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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();
}
}
}

View File

@@ -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>

View File

@@ -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"/>

View File

@@ -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>

View File

@@ -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();

View File

@@ -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>

View File

@@ -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));
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -211,6 +211,7 @@ public class DefaultHttpHeaderMapper implements HeaderMapper<HttpHeaders>, BeanF
AGE,
ALLOW,
CACHE_CONTROL,
CONNECTION,
CONTENT_ENCODING,
CONTENT_LANGUAGE,
CONTENT_LENGTH,

View File

@@ -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();

View File

@@ -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;
}
/**

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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");

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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";
}

View File

@@ -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();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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());

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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)

View File

@@ -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();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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();
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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();
}

View File

@@ -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();
}
}

View File

@@ -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) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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 {

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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"/>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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(

View File

@@ -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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. 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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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">

View File

@@ -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;
}

View File

@@ -267,6 +267,4 @@ public class XPathRouterParserTests {
}
}
public static class MyChannelResolver extends BeanFactoryChannelResolver{}
}

View File

@@ -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>

View File

@@ -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";

View File

@@ -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();

View File

@@ -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>

View File

@@ -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.

View File

@@ -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>

View 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>&lt;int:inbound-channel-adapter&gt;</code>
allows polling a java method for data; the
<code>&lt;int:outbound-channel-adapter&gt;</code> allows sending data to a <code>void</code> method, and
as discussed in <xref linkend="gateway" />, the <code>&lt;int:gateway&gt;</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>

View File

@@ -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>

View File

@@ -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"

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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"/>

View 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>

View File

@@ -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>

View File

@@ -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.

View File

@@ -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"/>

View File

@@ -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>

View File

@@ -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>&lt;int-ip:tcp-connection-event-inbound-channel-adapter/&gt;</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>&lt;int-jmx:tree-polling-channel-adapter/&gt;</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>&lt;int:inbound-channel-adapter/&gt;</code> now supports <code>&lt;expression/&gt;</code>
and <code>&lt;script/&gt;</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>&lt;header/&gt;</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>&lt;request-mapping/&gt;</code>
sub-element for the <code>&lt;http:inbound-channel-adapter/&gt;</code> and the <code>&lt;http:inbound-gateway/&gt;</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>&lt;request-mapping/&gt;</code>
sub-element for <code>&lt;http:inbound-channel-adapter/&gt;</code> and <code>&lt;http:inbound-gateway/&gt;</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>&lt;int-jmx:tree-polling-channel-adapter/&gt;</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>&lt;int-ip:tcp-connection-event-inbound-channel-adapter/&gt;</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>&lt;int:inbound-channel-adapter/&gt;</code> now supports <code>&lt;expression/&gt;</code>
and <code>&lt;script/&gt;</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>&lt;header/&gt;</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>&lt;gateway&gt; 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 &lt;filter/&gt; had a &lt;request-handler-advice-chain/&gt;, 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>&lt;uri-variable/&gt;</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 &lt;int-amqp:outbound-gateway/&gt; 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>&lt;message-driven-channel-adapter/&gt;</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>&lt;sql-parameter-definition/&gt;</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>&lt;returning-resultset/&gt;</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>&lt;int-event:inbound-channel-adapter&gt;</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 &lt;publish-subscribe-channel/&gt; 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>&lt;delayer&gt;</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>&lt;delayer&gt;</code> with a <code>&lt;header-enricher&gt;</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 &lt;int-amqp:outbound-gateway/&gt; 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 &lt;publish-subscribe-channel/&gt; 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>&lt;sql-parameter-definition/&gt;</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>&lt;returning-resultset/&gt;</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>&lt;uri-variable/&gt;</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 &lt;filter/&gt; had a &lt;request-handler-advice-chain/&gt;, 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>&lt;delayer&gt;</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>&lt;delayer&gt;</code> with a <code>&lt;header-enricher&gt;</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>&lt;int-event:inbound-channel-adapter&gt;</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>&lt;message-driven-channel-adapter/&gt;</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>

View File

@@ -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>