Merge pull request #106 from garyrussell/INT-2162

INT-2162 Add apply-sequence to TCP Connection Factory
  INT-2154 Refactor TCP ConnectionFactorys to facilitate subclassing
  INT-2050 Code Polishing
This commit is contained in:
Mark Fisher
2011-10-04 14:41:18 -04:00
46 changed files with 1487 additions and 758 deletions

View File

@@ -652,7 +652,7 @@
Two sequential
messages arriving on the <emphasis>same</emphasis> socket <emphasis>might</emphasis>
be processed by different threads. This means that the order in which the messages are
sent to the channel is indeterminate; the strict ordering of the messages on the
sent to the channel is indeterminate; the strict ordering of the messages arriving on the
socket is not maintained.
</para>
<para>
@@ -660,6 +660,14 @@
is required, consider setting <classname>using-nio</classname> to false
and using async handoff.
</para>
<para>
Alternatively, you may choose to insert a resequencer downstream of the inbound endpoint to
return the messages to their proper sequence. Set <emphasis>apply-sequence</emphasis>
to true on the connection factory, and messages arriving on a TCP connection will
have <emphasis>sequenceNumber</emphasis> and <emphasis>correlationId</emphasis> headers
set. The resequencer uses these headers to return the messages to their proper
sequence.
</para>
</section>
<section id="ip-endpoint-reference">
<title>IP Configuration Attributes</title>
@@ -727,7 +735,7 @@
<entry>true, false</entry>
<entry>Whether or not connection uses NIO. Refer to the java.nio
package for more information.
See <xref linkend="note_nio" />.
See <xref linkend="note_nio" />.
Default false.</entry>
</row>
<row>
@@ -739,6 +747,18 @@
Refer to <classname>java.nio.ByteBuffer</classname> documentation for
more information. Must be false if using-nio is false. </entry>
</row>
<row>
<entry>apply-sequence</entry>
<entry>Y</entry>
<entry>Y</entry>
<entry>true, false</entry>
<entry>When using NIO, it may be necessary to resequence messages. When this
attribute is set to true, <emphasis>correlationId</emphasis> and
<emphasis>sequenceNumber</emphasis> headers will be added to
received messages.
See <xref linkend="note_nio" />.
Default false.</entry>
</row>
<row>
<entry>so-timeout</entry>
<entry>Y</entry>

View File

@@ -1,13 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beansProjectDescription>
<version>1</version>
<pluginVersion><![CDATA[2.5.1.201011101000-RELEASE]]></pluginVersion>
<pluginVersion><![CDATA[2.7.2.201109122348-RELEASE]]></pluginVersion>
<configSuffixes>
<configSuffix><![CDATA[xml]]></configSuffix>
</configSuffixes>
<enableImports><![CDATA[false]]></enableImports>
<configs>
<config>src/test/java/org/springframework/integration/ip/tcp/connection/SOLingerTests-context.xml</config>
<config>src/test/java/org/springframework/integration/ip/tcp/AutoStartTests-context.xml</config>
</configs>
<configSets>
</configSets>

View File

@@ -35,23 +35,23 @@ import org.springframework.util.Assert;
public abstract class AbstractInternetProtocolReceivingChannelAdapter
extends MessageProducerSupport implements Runnable, CommonSocketOptions {
protected final int port;
private final int port;
protected volatile int soTimeout = 0;
private volatile int soTimeout = 0;
protected volatile int soReceiveBufferSize = -1;
private volatile int soReceiveBufferSize = -1;
protected volatile int receiveBufferSize = 2048;
private volatile int receiveBufferSize = 2048;
protected volatile boolean active;
private volatile boolean active;
protected volatile boolean listening;
private volatile boolean listening;
protected volatile String localAddress;
private volatile String localAddress;
protected volatile Executor taskExecutor;
private volatile Executor taskExecutor;
protected volatile int poolSize = 5;
private volatile int poolSize = 5;
public AbstractInternetProtocolReceivingChannelAdapter(int port) {
@@ -66,30 +66,39 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
return port;
}
/* (non-Javadoc)
* @see org.springframework.integration.ip.SocketOptions#setSoTimeout(int)
*/
public void setSoTimeout(int soTimeout) {
this.soTimeout = soTimeout;
}
/* (non-Javadoc)
* @see org.springframework.integration.ip.SocketOptions#setSoReceiveBufferSize(int)
/**
* @return the soTimeout
*/
public int getSoTimeout() {
return soTimeout;
}
public void setSoReceiveBufferSize(int soReceiveBufferSize) {
this.soReceiveBufferSize = soReceiveBufferSize;
}
/* (non-Javadoc)
* @see org.springframework.integration.ip.CommonSocketOptions#setSoSendBufferSize(int)
/**
* @return the soReceiveBufferSize
*/
public void setSoSendBufferSize(int soSendBufferSize) {
public int getSoReceiveBufferSize() {
return soReceiveBufferSize;
}
public void setReceiveBufferSize(int receiveBufferSize) {
this.receiveBufferSize = receiveBufferSize;
}
/**
* @return the receiveBufferSize
*/
public int getReceiveBufferSize() {
return receiveBufferSize;
}
@Override
protected void doStart() {
TaskScheduler taskScheduler = this.getTaskScheduler();
@@ -117,9 +126,6 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
}
}
/* (non-Javadoc)
* @see org.springframework.integration.endpoint.AbstractEndpoint#doStop()
*/
@Override
protected void doStop() {
this.active = false;
@@ -129,6 +135,13 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
return listening;
}
/**
* @param listening the listening to set
*/
public void setListening(boolean listening) {
this.listening = listening;
}
public String getLocalAddress() {
return localAddress;
}
@@ -145,4 +158,18 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
this.taskExecutor = taskExecutor;
}
/**
* @return the taskExecutor
*/
public Executor getTaskExecutor() {
return taskExecutor;
}
/**
* @return the active
*/
public boolean isActive() {
return active;
}
}

View File

@@ -36,15 +36,15 @@ public abstract class AbstractInternetProtocolSendingMessageHandler extends Abst
protected final Log logger = LogFactory.getLog(getClass());
protected final SocketAddress destinationAddress;
private final SocketAddress destinationAddress;
protected final String host;
private final String host;
protected final int port;
private final int port;
protected volatile int soSendBufferSize = -1;
private volatile int soSendBufferSize = -1;
protected volatile int soTimeout = -1;
private volatile int soTimeout = -1;
public AbstractInternetProtocolSendingMessageHandler(String host, int port) {
Assert.notNull(host, "host must not be null");
@@ -80,6 +80,14 @@ public abstract class AbstractInternetProtocolSendingMessageHandler extends Abst
this.soSendBufferSize = size;
}
/**
* @return the host
*/
public String getHost() {
return host;
}
/**
* @return the port
*/
@@ -87,4 +95,28 @@ public abstract class AbstractInternetProtocolSendingMessageHandler extends Abst
return port;
}
/**
* @return the destinationAddress
*/
public SocketAddress getDestinationAddress() {
return destinationAddress;
}
/**
* @return the soTimeout
*/
public int getSoTimeout() {
return soTimeout;
}
/**
* @return the soSendBufferSize
*/
public int getSoSendBufferSize() {
return soSendBufferSize;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2001-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,19 +29,19 @@ public interface CommonSocketOptions {
* @see Socket#setSoTimeout(int)
* @see DatagramSocket#setSoTimeout(int)
*/
public void setSoTimeout(int soTimeout);
void setSoTimeout(int soTimeout);
/**
* @see Socket#setReceiveBufferSize(int)
* @see DatagramSocket#setReceiveBufferSize(int)
*/
public void setSoReceiveBufferSize(int soReceiveBufferSize);
void setSoReceiveBufferSize(int soReceiveBufferSize);
/**
* @see Socket#setSendBufferSize(int)
* @see DatagramSocket#setSendBufferSize(int)
*/
public void setSoSendBufferSize(int soSendBufferSize);
void setSoSendBufferSize(int soSendBufferSize);
/**
* On a multi-homed system, specifies the ip address of the network interface used to communicate.
@@ -53,6 +53,6 @@ public interface CommonSocketOptions {
*
* @param localAddress
*/
public void setLocalAddress(String localAddress);
void setLocalAddress(String localAddress);
}

View File

@@ -42,6 +42,11 @@ public abstract class IpHeaders {
public static final String CONNECTION_ID = IP + "connection_id";
/**
* Use apply-sequence and sequenceNumber instead
* @deprecated
*/
@Deprecated
public static final String CONNECTION_SEQ = IP + "connection_seq";
}

View File

@@ -107,6 +107,11 @@ public abstract class IpAdapterParserUtils {
public static final String LOOKUP_HOST = "lookup-host";
public static final String AUTO_STARTUP = "auto-startup";
public static final String PHASE = "phase";
public static final String APPLY_SEQUENCE = "apply-sequence";
/**
* Adds a constructor-arg to the provided bean definition builder

View File

@@ -17,6 +17,7 @@ package org.springframework.integration.ip.config;
import java.util.concurrent.Executor;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.context.SmartLifecycle;
import org.springframework.core.serializer.Deserializer;
@@ -24,13 +25,11 @@ import org.springframework.core.serializer.Serializer;
import org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory;
import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorFactoryChain;
import org.springframework.integration.ip.tcp.connection.TcpListener;
import org.springframework.integration.ip.tcp.connection.TcpMessageMapper;
import org.springframework.integration.ip.tcp.connection.TcpNetClientConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpNetServerConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpNioClientConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpNioServerConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpSender;
import org.springframework.integration.ip.tcp.serializer.ByteArrayCrLfSerializer;
/**
@@ -42,58 +41,56 @@ import org.springframework.integration.ip.tcp.serializer.ByteArrayCrLfSerializer
*
*/
public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<AbstractConnectionFactory>
implements SmartLifecycle {
implements SmartLifecycle, BeanNameAware {
private AbstractConnectionFactory connectionFactory;
private String type;
private volatile AbstractConnectionFactory connectionFactory;
protected String host;
protected int port;
protected TcpListener listener;
private volatile String type;
protected TcpSender sender;
private volatile String host;
protected int soTimeout;
private volatile int port;
private int soSendBufferSize;
private volatile int soTimeout;
private int soReceiveBufferSize;
private boolean soTcpNoDelay;
private volatile int soSendBufferSize;
private int soLinger = -1; // don't set by default
private volatile int soReceiveBufferSize;
private boolean soKeepAlive;
private volatile boolean soTcpNoDelay;
private int soTrafficClass = -1; // don't set by default
private Executor taskExecutor;
protected Deserializer<?> deserializer = new ByteArrayCrLfSerializer();
protected Serializer<?> serializer = new ByteArrayCrLfSerializer();
protected TcpMessageMapper mapper = new TcpMessageMapper();
private volatile int soLinger = -1; // don't set by default
protected boolean singleUse;
private volatile boolean soKeepAlive;
protected int poolSize = 5;
private volatile int soTrafficClass = -1; // don't set by default
protected volatile boolean active;
private volatile Executor taskExecutor;
protected TcpConnectionInterceptorFactoryChain interceptorFactoryChain;
private boolean lookupHost = true;
private String localAddress;
private volatile Deserializer<?> deserializer = new ByteArrayCrLfSerializer();
private volatile Serializer<?> serializer = new ByteArrayCrLfSerializer();
private volatile TcpMessageMapper mapper = new TcpMessageMapper();
private volatile boolean singleUse;
private volatile int poolSize = 5;
private volatile TcpConnectionInterceptorFactoryChain interceptorFactoryChain;
private volatile boolean lookupHost = true;
private volatile String localAddress;
private volatile boolean usingNio;
private volatile boolean usingDirectBuffers;
private volatile String beanName;
private volatile boolean applySequence;
private boolean usingNio;
private boolean usingDirectBuffers;
@Override
public Class<?> getObjectType() {
return this.connectionFactory != null ? this.connectionFactory.getClass()
@@ -136,6 +133,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
factory.setDeserializer(this.deserializer);
factory.setInterceptorFactoryChain(this.interceptorFactoryChain);
factory.setLookupHost(this.lookupHost);
this.mapper.setApplySequence(this.applySequence);
factory.setMapper(this.mapper);
factory.setPoolSize(this.poolSize);
factory.setSerializer(this.serializer);
@@ -148,6 +146,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
factory.setSoTimeout(this.soTimeout);
factory.setSoTrafficClass(this.soTrafficClass);
factory.setTaskExecutor(this.taskExecutor);
factory.setBeanName(this.beanName);
}
private void setServerAttributes(AbstractServerConnectionFactory factory) {
@@ -363,5 +362,16 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
return this.connectionFactory.isRunning();
}
public void setBeanName(String name) {
this.beanName = name;
}
/**
* @param applySequence the applySequence to set
*/
public void setApplySequence(boolean applySequence) {
this.applySequence = applySequence;
}
}

View File

@@ -81,6 +81,8 @@ public class TcpConnectionParser extends AbstractBeanDefinitionParser {
IpAdapterParserUtils.INTERCEPTOR_FACTORY_CHAIN);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
IpAdapterParserUtils.LOOKUP_HOST);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
IpAdapterParserUtils.APPLY_SEQUENCE);
return builder.getBeanDefinition();
}

View File

@@ -42,6 +42,10 @@ public class TcpInboundChannelAdapterParser extends AbstractChannelAdapterParser
element, "channel", "outputChannel");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder,
element, "error-channel", "errorChannel");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
IpAdapterParserUtils.AUTO_STARTUP);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
IpAdapterParserUtils.PHASE);
return builder.getBeanDefinition();
}

View File

@@ -36,6 +36,10 @@ public class TcpOutboundChannelAdapterParser extends AbstractOutboundChannelAdap
".TcpSendingMessageHandler");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element,
IpAdapterParserUtils.TCP_CONNECTION_FACTORY);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
IpAdapterParserUtils.AUTO_STARTUP);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
IpAdapterParserUtils.PHASE);
return builder.getBeanDefinition();
}

View File

@@ -49,6 +49,10 @@ public class TcpOutboundGatewayParser extends AbstractConsumerEndpointParser {
IpAdapterParserUtils.REQUEST_TIMEOUT);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
IpAdapterParserUtils.REPLY_TIMEOUT);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
IpAdapterParserUtils.AUTO_STARTUP);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
IpAdapterParserUtils.PHASE);
return builder;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@ import org.springframework.integration.ip.tcp.connection.TcpSender;
*/
public class TcpInboundGateway extends MessagingGatewaySupport implements TcpListener, TcpSender {
protected AbstractServerConnectionFactory connectionFactory;
private AbstractServerConnectionFactory connectionFactory;
private Map<String, TcpConnection> connections = new ConcurrentHashMap<String, TcpConnection>();
@@ -94,4 +94,11 @@ public class TcpInboundGateway extends MessagingGatewaySupport implements TcpLis
public String getComponentType(){
return "ip:tcp-inbound-gateway";
}
/**
* @return the connectionFactory
*/
protected AbstractServerConnectionFactory getConnectionFactory() {
return connectionFactory;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2001-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.springframework.context.SmartLifecycle;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageTimeoutException;
@@ -35,28 +36,34 @@ import org.springframework.integration.ip.tcp.connection.TcpListener;
import org.springframework.integration.ip.tcp.connection.TcpSender;
import org.springframework.util.Assert;
/**
/**
* TCP outbound gateway that uses a client connection factory. If the factory is configured
* for single-use connections, each request is sent on a new connection; if the factory does not use
* single use connections, each request is blocked until the previous response is received
* (or times out). Asynchronous requests/responses over the same connection are not
* (or times out). Asynchronous requests/responses over the same connection are not
* supported - use a pair of outbound/inbound adapters for that use case.
*
* <p/>
* {@link SmartLifecycle} methods delegate to the underlying {@link AbstractConnectionFactory}
*
*
* @author Gary Russell
* @since 2.0
*/
public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler implements TcpSender, TcpListener {
public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler implements TcpSender, TcpListener, SmartLifecycle {
private volatile AbstractConnectionFactory connectionFactory;
protected AbstractConnectionFactory connectionFactory;
private Map<String, AsyncReply> pendingReplies = new ConcurrentHashMap<String, AsyncReply>();
private Semaphore semaphore = new Semaphore(1, true);
private long replyTimeout = 10000;
private long requestTimeout = 10000;
private volatile long replyTimeout = 10000;
private volatile long requestTimeout = 10000;
private volatile boolean autoStartup = true;
private volatile int phase;
/**
* @param requestTimeout the requestTimeout to set
@@ -98,7 +105,7 @@ public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler imp
connection.send(requestMessage);
Message<?> replyMessage = reply.getReply();
if (replyMessage == null) {
throw new MessageTimeoutException(requestMessage, "Timed out waiting for response");
throw new MessageTimeoutException(requestMessage, "Timed out waiting for response");
}
if (logger.isDebugEnabled()) {
logger.debug("Respose " + replyMessage);
@@ -137,12 +144,8 @@ public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler imp
return false;
}
public boolean isListening() {
return false;
}
public void setConnectionFactory(AbstractConnectionFactory connectionFactory) {
Assert.isTrue(connectionFactory instanceof AbstractClientConnectionFactory,
Assert.isTrue(connectionFactory instanceof AbstractClientConnectionFactory,
this.getClass().getName() + " requires a client connection factory");
this.connectionFactory = connectionFactory;
connectionFactory.registerListener(this);
@@ -157,10 +160,59 @@ public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler imp
// do nothing - no asynchronous multiplexing supported
}
/**
* Specify the Spring Integration reply channel. If this property is not
* set the gateway will check for a 'replyChannel' header on the request.
*/
public void setReplyChannel(MessageChannel replyChannel) {
this.setOutputChannel(replyChannel);
}
public String getComponentType(){
return "ip:tcp-outbound-gateway";
}
public void start() {
this.connectionFactory.start();
}
public void stop() {
this.connectionFactory.stop();
}
public boolean isRunning() {
return this.connectionFactory.isRunning();
}
public int getPhase() {
return this.phase;
}
public boolean isAutoStartup() {
return this.autoStartup;
}
public void stop(Runnable callback) {
this.connectionFactory.stop(callback);
}
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
public void setPhase(int phase) {
this.phase = phase;
}
/**
* @return the connectionFactory
*/
protected AbstractConnectionFactory getConnectionFactory() {
return connectionFactory;
}
/**
* Class used to coordinate the asynchronous reply to its request.
*
*
* @author Gary Russell
* @since 2.0
*/
@@ -197,14 +249,4 @@ public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler imp
}
}
/**
* Specify the Spring Integration reply channel. If this property is not
* set the gateway will check for a 'replyChannel' header on the request.
*/
public void setReplyChannel(MessageChannel replyChannel) {
this.setOutputChannel(replyChannel);
}
public String getComponentType(){
return "ip:tcp-outbound-gateway";
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,8 +15,6 @@
*/
package org.springframework.integration.ip.tcp;
import java.net.ServerSocket;
import org.springframework.integration.Message;
import org.springframework.integration.endpoint.MessageProducerSupport;
import org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory;
@@ -38,11 +36,9 @@ import org.springframework.integration.ip.tcp.connection.TcpListener;
public class TcpReceivingChannelAdapter
extends MessageProducerSupport implements TcpListener {
protected ServerSocket serverSocket;
private ConnectionFactory clientConnectionFactory;
protected ConnectionFactory clientConnectionFactory;
protected ConnectionFactory serverConnectionFactory;
private ConnectionFactory serverConnectionFactory;
public boolean onMessage(Message<?> message) {
sendMessage(message);
@@ -51,12 +47,22 @@ public class TcpReceivingChannelAdapter
@Override
protected void doStart() {
// Nothing to do; we're passive
if (this.serverConnectionFactory != null) {
this.serverConnectionFactory.start();
}
if (this.clientConnectionFactory != null) {
this.clientConnectionFactory.start();
}
}
@Override
protected void doStop() {
// Nothing to do; we're passive
if (this.clientConnectionFactory != null) {
this.clientConnectionFactory.stop();
}
if (this.serverConnectionFactory != null) {
this.serverConnectionFactory.stop();
}
}
/**
@@ -88,4 +94,18 @@ public class TcpReceivingChannelAdapter
public String getComponentType(){
return "ip:tcp-inbound-channel-adapter";
}
/**
* @return the clientConnectionFactory
*/
protected ConnectionFactory getClientConnectionFactory() {
return clientConnectionFactory;
}
/**
* @return the serverConnectionFactory
*/
protected ConnectionFactory getServerConnectionFactory() {
return serverConnectionFactory;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.SmartLifecycle;
import org.springframework.integration.Message;
import org.springframework.integration.MessageDeliveryException;
import org.springframework.integration.MessageHandlingException;
@@ -35,7 +36,7 @@ import org.springframework.integration.ip.tcp.connection.TcpSender;
import org.springframework.integration.mapping.MessageMappingException;
/**
* Tcp outbound channel adapter using a TcpConnection to
* Tcp outbound channel adapter using a TcpConnection to
* send data - if the connection factory is a server
* factory, the TcpListener owns the connections. If it is
* a client factory, this object owns the connection.
@@ -43,39 +44,36 @@ import org.springframework.integration.mapping.MessageMappingException;
* @since 2.0
*
*/
public class TcpSendingMessageHandler extends AbstractMessageHandler implements TcpSender {
protected Log logger = LogFactory.getLog(this.getClass());
protected TcpConnection connection;
protected ConnectionFactory clientConnectionFactory;
protected ConnectionFactory serverConnectionFactory;
protected Map<String, TcpConnection> connections = new ConcurrentHashMap<String, TcpConnection>();
protected synchronized TcpConnection getConnection() {
public class TcpSendingMessageHandler extends AbstractMessageHandler implements TcpSender, SmartLifecycle {
protected final Log logger = LogFactory.getLog(this.getClass());
private volatile ConnectionFactory clientConnectionFactory;
private volatile ConnectionFactory serverConnectionFactory;
private Map<String, TcpConnection> connections = new ConcurrentHashMap<String, TcpConnection>();
private volatile boolean autoStartup;
private volatile int phase;
protected TcpConnection getConnection() {
TcpConnection connection = null;
if (this.clientConnectionFactory == null) {
return null;
}
try {
this.connection = clientConnectionFactory.getConnection();
connection = this.clientConnectionFactory.getConnection();
} catch (Exception e) {
logger.error("Error creating SocketWriter", e);
}
return this.connection;
}
/**
* Close the underlying socket and prepare to establish a new socket on
* the next write.
*/
protected void close() {
this.connection.close();
this.connection = null;
return connection;
}
/**
* Writes the message payload to the underlying socket, using the specified
* message format.
* message format.
* @see org.springframework.integration.core.MessageHandler#handleMessage(org.springframework.integration.Message)
*/
public void handleMessageInternal(final Message<?> message) throws MessageRejectedException,
@@ -96,7 +94,7 @@ public class TcpSendingMessageHandler extends AbstractMessageHandler implements
}
return;
}
// we own the connection
try {
doWrite(message);
@@ -116,8 +114,9 @@ public class TcpSendingMessageHandler extends AbstractMessageHandler implements
* @param message The message to write.
*/
protected void doWrite(Message<?> message) {
TcpConnection connection = null;
try {
TcpConnection connection = getConnection();
connection = getConnection();
if (connection == null) {
throw new MessageMappingException(message, "Failed to create connection");
}
@@ -126,11 +125,10 @@ public class TcpSendingMessageHandler extends AbstractMessageHandler implements
}
connection.send(message);
} catch (Exception e) {
String connectionId = null;
if (this.connection != null) {
connectionId = this.connection.getConnectionId();
String connectionId = null;
if (connection != null) {
connectionId = connection.getConnectionId();
}
this.connection = null;
if (e instanceof MessageMappingException) {
throw (MessageMappingException) e;
}
@@ -142,7 +140,7 @@ public class TcpSendingMessageHandler extends AbstractMessageHandler implements
* Sets the client or server connection factory; for this (an outbound adapter), if
* the factory is a server connection factory, the sockets are owned by a receiving
* channel adapter and this adapter is used to send replies.
*
*
* @param connectionFactory the connectionFactory to set
*/
public void setConnectionFactory(AbstractConnectionFactory connectionFactory) {
@@ -157,11 +155,83 @@ public class TcpSendingMessageHandler extends AbstractMessageHandler implements
public void addNewConnection(TcpConnection connection) {
connections.put(connection.getConnectionId(), connection);
}
public void removeDeadConnection(TcpConnection connection) {
connections.remove(connection.getConnectionId());
}
public String getComponentType(){
return "ip:tcp-outbound-channel-adapter";
}
public void start() {
if (this.clientConnectionFactory != null) {
this.clientConnectionFactory.start();
}
if (this.serverConnectionFactory != null) {
this.serverConnectionFactory.start();
}
}
public void stop() {
if (this.clientConnectionFactory != null) {
this.clientConnectionFactory.stop();
}
if (this.serverConnectionFactory != null) {
this.serverConnectionFactory.stop();
}
}
public boolean isRunning() {
boolean cfRunning = this.clientConnectionFactory != null ? this.clientConnectionFactory.isRunning() : false;
boolean sfRunning = this.serverConnectionFactory != null ? this.serverConnectionFactory.isRunning() : false;
return cfRunning | sfRunning;
}
public int getPhase() {
return this.phase;
}
public boolean isAutoStartup() {
return this.autoStartup;
}
public void stop(Runnable callback) {
if (this.clientConnectionFactory != null) {
this.clientConnectionFactory.stop(callback);
}
if (this.serverConnectionFactory != null) {
this.serverConnectionFactory.stop(callback);
}
}
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
public void setPhase(int phase) {
this.phase = phase;
}
/**
* @return the clientConnectionFactory
*/
protected ConnectionFactory getClientConnectionFactory() {
return clientConnectionFactory;
}
/**
* @return the serverConnectionFactory
*/
protected ConnectionFactory getServerConnectionFactory() {
return serverConnectionFactory;
}
/**
* @return the connections
*/
protected Map<String, TcpConnection> getConnections() {
return connections;
}
}

View File

@@ -19,8 +19,6 @@ package org.springframework.integration.ip.tcp.connection;
import java.net.Socket;
import java.net.SocketException;
import org.springframework.util.Assert;
/**
* Abstract class for client connection factories; client connection factories
* establish outgoing connections.
@@ -30,7 +28,7 @@ import org.springframework.util.Assert;
*/
public abstract class AbstractClientConnectionFactory extends AbstractConnectionFactory {
protected TcpConnection theConnection;
private TcpConnection theConnection;
/**
* Constructs a factory that will established connections to the host and port.
@@ -38,9 +36,7 @@ public abstract class AbstractClientConnectionFactory extends AbstractConnection
* @param port The port.
*/
public AbstractClientConnectionFactory(String host, int port) {
Assert.notNull(host, "host must not be null");
this.host = host;
this.port = port;
super(host, port);
}
/**
@@ -53,11 +49,12 @@ public abstract class AbstractClientConnectionFactory extends AbstractConnection
* @param socket The new socket.
*/
protected void initializeConnection(TcpConnection connection, Socket socket) {
if (this.listener != null) {
connection.registerListener(this.listener);
TcpListener listener = this.getListener();
if (listener != null) {
connection.registerListener(listener);
}
if (this.listener != null || this.singleUse) {
if (this.soTimeout <= 0) {
if (listener != null || this.isSingleUse()) {
if (this.getSoTimeout() <= 0) {
try {
socket.setSoTimeout(DEFAULT_REPLY_TIMEOUT);
} catch (SocketException e) {
@@ -65,10 +62,24 @@ public abstract class AbstractClientConnectionFactory extends AbstractConnection
}
}
}
connection.setMapper(this.mapper);
connection.setDeserializer(this.deserializer);
connection.setSerializer(this.serializer);
connection.setSingleUse(this.singleUse);
connection.setMapper(this.getMapper());
connection.setDeserializer(this.getDeserializer());
connection.setSerializer(this.getSerializer());
connection.setSingleUse(this.isSingleUse());
}
/**
* @param theConnection the theConnection to set
*/
protected void setTheConnection(TcpConnection theConnection) {
this.theConnection = theConnection;
}
/**
* @return the theConnection
*/
protected TcpConnection getTheConnection() {
return theConnection;
}
}

View File

@@ -36,73 +36,85 @@ import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.SmartLifecycle;
import org.springframework.core.serializer.Deserializer;
import org.springframework.core.serializer.Serializer;
import org.springframework.integration.MessagingException;
import org.springframework.integration.context.IntegrationObjectSupport;
import org.springframework.integration.ip.tcp.serializer.ByteArrayCrLfSerializer;
import org.springframework.util.Assert;
/**
* Base class for all connection factories.
*
*
* @author Gary Russell
* @since 2.0
*
*/
public abstract class AbstractConnectionFactory
implements ConnectionFactory, Runnable, SmartLifecycle {
public abstract class AbstractConnectionFactory extends IntegrationObjectSupport
implements ConnectionFactory, Runnable, SmartLifecycle, BeanNameAware {
protected Log logger = LogFactory.getLog(this.getClass());
protected final static int DEFAULT_REPLY_TIMEOUT = 10000;
protected String host;
protected int port;
protected TcpListener listener;
protected final Log logger = LogFactory.getLog(this.getClass());
protected TcpSender sender;
protected static final int DEFAULT_REPLY_TIMEOUT = 10000;
protected int soTimeout;
private volatile String host;
private int soSendBufferSize;
private volatile int port;
private int soReceiveBufferSize;
private boolean soTcpNoDelay;
private volatile TcpListener listener;
private int soLinger = -1; // don't set by default
private volatile TcpSender sender;
private boolean soKeepAlive;
private volatile int soTimeout;
private int soTrafficClass = -1; // don't set by default
private Executor taskExecutor;
private boolean privateExecutor;
private volatile int soSendBufferSize;
protected Deserializer<?> deserializer = new ByteArrayCrLfSerializer();
protected Serializer<?> serializer = new ByteArrayCrLfSerializer();
protected TcpMessageMapper mapper = new TcpMessageMapper();
private volatile int soReceiveBufferSize;
protected boolean singleUse;
private volatile boolean soTcpNoDelay;
protected int poolSize = 5;
private volatile int soLinger = -1; // don't set by default
protected volatile boolean active;
private volatile boolean soKeepAlive;
protected TcpConnectionInterceptorFactoryChain interceptorFactoryChain;
private boolean lookupHost = true;
private List<TcpConnection> connections = new LinkedList<TcpConnection>();
private volatile int soTrafficClass = -1; // don't set by default
private volatile Executor taskExecutor;
private volatile boolean privateExecutor;
private volatile Deserializer<?> deserializer = new ByteArrayCrLfSerializer();
private volatile Serializer<?> serializer = new ByteArrayCrLfSerializer();
private volatile TcpMessageMapper mapper = new TcpMessageMapper();
private volatile boolean singleUse;
private volatile int poolSize = 5;
private volatile boolean active;
private volatile TcpConnectionInterceptorFactoryChain interceptorFactoryChain;
private volatile boolean lookupHost = true;
private volatile List<TcpConnection> connections = new LinkedList<TcpConnection>();
protected final Object lifecycleMonitor = new Object();
public AbstractConnectionFactory(int port) {
this.port = port;
}
public AbstractConnectionFactory(String host, int port) {
Assert.notNull(host, "host must not be null");
this.host = host;
this.port = port;
}
/**
* Sets socket attributes on the socket.
* @param socket The socket.
@@ -240,6 +252,48 @@ public abstract class AbstractConnectionFactory
return port;
}
/**
* @return the listener
*/
public TcpListener getListener() {
return listener;
}
/**
* @return the sender
*/
public TcpSender getSender() {
return sender;
}
/**
* @return the serializer
*/
public Serializer<?> getSerializer() {
return serializer;
}
/**
* @return the deserializer
*/
public Deserializer<?> getDeserializer() {
return deserializer;
}
/**
* @return the mapper
*/
public TcpMessageMapper getMapper() {
return mapper;
}
/**
* @return the poolSize
*/
public int getPoolSize() {
return poolSize;
}
/**
* Registers a TcpListener to receive messages after
* the payload has been converted from the input data.
@@ -252,7 +306,7 @@ public abstract class AbstractConnectionFactory
}
/**
* Registers a TcpSender; for server sockets, used to
* Registers a TcpSender; for server sockets, used to
* provide connection information so a sender can be used
* to reply to incoming messages.
* @param sender The sender
@@ -271,7 +325,7 @@ public abstract class AbstractConnectionFactory
}
/**
*
*
* @param deserializer the deserializer to set
*/
public void setDeserializer(Deserializer<?> deserializer) {
@@ -279,7 +333,7 @@ public abstract class AbstractConnectionFactory
}
/**
*
*
* @param serializer the serializer to set
*/
public void setSerializer(Serializer<?> serializer) {
@@ -287,7 +341,7 @@ public abstract class AbstractConnectionFactory
}
/**
*
*
* @param mapper the mapper to set; defaults to a {@link TcpMessageMapper}
*/
public void setMapper(TcpMessageMapper mapper) {
@@ -309,7 +363,7 @@ public abstract class AbstractConnectionFactory
this.singleUse = singleUse;
}
public void setPoolSize(int poolSize) {
this.poolSize = poolSize;
}
@@ -349,10 +403,13 @@ public abstract class AbstractConnectionFactory
this.getTaskExecutor().execute(this);
}
}
if (logger.isInfoEnabled()) {
logger.info("started " + this);
}
}
/**
* Creates a taskExecutor (if one was not provided).
* Creates a taskExecutor (if one was not provided).
*/
protected Executor getTaskExecutor() {
synchronized (this.lifecycleMonitor) {
@@ -388,7 +445,7 @@ public abstract class AbstractConnectionFactory
try {
if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
logger.debug("Forcing executor shutdown");
executorService.shutdownNow();
executorService.shutdownNow();
if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
logger.debug("Executor failed to shutdown");
}
@@ -402,6 +459,9 @@ public abstract class AbstractConnectionFactory
}
}
}
if (logger.isInfoEnabled()) {
logger.info("stopped " + this);
}
}
protected TcpConnection wrapConnection(TcpConnection connection) throws Exception {
@@ -409,7 +469,7 @@ public abstract class AbstractConnectionFactory
if (this.interceptorFactoryChain == null) {
return connection;
}
TcpConnectionInterceptorFactory[] interceptorFactories =
TcpConnectionInterceptorFactory[] interceptorFactories =
this.interceptorFactoryChain.getInterceptorFactories();
if (interceptorFactories == null) {
return connection;
@@ -433,9 +493,9 @@ public abstract class AbstractConnectionFactory
}
/**
*
*
* Times out any expired connections then, if selectionCount > 0, processes the selected keys.
*
*
* @param selectionCount
* @param selector
* @param connections
@@ -483,7 +543,7 @@ public abstract class AbstractConnectionFactory
else if (key.isReadable()) {
try {
key.interestOps(key.interestOps() - key.readyOps());
final TcpNioConnection connection;
final TcpNioConnection connection;
connection = (TcpNioConnection) key.attachment();
connection.setLastRead(System.currentTimeMillis());
this.taskExecutor.execute(new Runnable() {
@@ -535,13 +595,17 @@ public abstract class AbstractConnectionFactory
protected void doAccept(final Selector selector, ServerSocketChannel server, long now) throws IOException {
throw new UnsupportedOperationException("Nio server factory must override this method");
}
public int getPhase() {
return 0;
}
/**
* We are controlled by the startup options of
* the bound endpoint.
*/
public boolean isAutoStartup() {
return true;
return false;
}
public void stop(Runnable callback) {
@@ -558,7 +622,7 @@ public abstract class AbstractConnectionFactory
this.connections.add(connection);
}
}
protected void harvestClosedConnections() {
synchronized (this.connections) {
Iterator<TcpConnection> iterator = this.connections.iterator();
@@ -570,4 +634,29 @@ public abstract class AbstractConnectionFactory
}
}
}
public boolean isRunning() {
return this.active;
}
/**
* @return the active
*/
protected boolean isActive() {
return active;
}
/**
* @param active the active to set
*/
protected void setActive(boolean active) {
this.active = active;
}
protected void checkActive() throws IOException {
if (!this.isActive()) {
throw new IOException(this + " connection factory has not been started");
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2001-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,9 +29,9 @@ import java.net.SocketException;
*/
public abstract class AbstractServerConnectionFactory extends AbstractConnectionFactory {
protected boolean listening;
private boolean listening;
protected String localAddress;
private String localAddress;
/**
@@ -39,7 +39,7 @@ public abstract class AbstractServerConnectionFactory extends AbstractConnection
* @param port
*/
public AbstractServerConnectionFactory(int port) {
this.port = port;
super(port);
}
@@ -51,6 +51,14 @@ public abstract class AbstractServerConnectionFactory extends AbstractConnection
throw new UnsupportedOperationException("Getting a connection from a server factory is not supported");
}
/**
* @param listening the listening to set
*/
protected void setListening(boolean listening) {
this.listening = listening;
}
/**
*
* @return true if the server is listening on the port.
@@ -66,20 +74,21 @@ public abstract class AbstractServerConnectionFactory extends AbstractConnection
* @param socket The new socket.
*/
protected void initializeConnection(TcpConnection connection, Socket socket) {
if (this.listener != null) {
connection.registerListener(this.listener);
TcpListener listener = this.getListener();
if (listener != null) {
connection.registerListener(listener);
}
connection.registerSender(this.sender);
connection.setMapper(this.mapper);
connection.setDeserializer(this.deserializer);
connection.setSerializer(this.serializer);
connection.setSingleUse(this.singleUse);
connection.registerSender(this.getSender());
connection.setMapper(this.getMapper());
connection.setDeserializer(this.getDeserializer());
connection.setSerializer(this.getSerializer());
connection.setSingleUse(this.isSingleUse());
/*
* If we have a collaborating outbound channel adapter and we are configured
* for single use; need to enforce a timeout on the socket so we will close
* it some period after the response was sent (timeout on the next read).
*/
if (this.singleUse && this.soTimeout <= 0 && this.listener != null) {
if (this.isSingleUse() && this.getSoTimeout() <= 0 && listener != null) {
try {
socket.setSoTimeout(DEFAULT_REPLY_TIMEOUT);
} catch (SocketException e) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2001-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.springframework.integration.ip.tcp.connection;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.logging.Log;
@@ -33,68 +34,62 @@ import org.springframework.util.Assert;
* Base class for TcpConnections. TcpConnections are established by
* client connection factories (outgoing) or server connection factories
* (incoming).
*
*
* @author Gary Russell
* @since 2.0
*
*/
public abstract class AbstractTcpConnection implements TcpConnection {
protected Log logger = LogFactory.getLog(this.getClass());
protected final Log logger = LogFactory.getLog(this.getClass());
@SuppressWarnings("rawtypes")
protected Deserializer deserializer;
private volatile Deserializer deserializer;
@SuppressWarnings("rawtypes")
protected Serializer serializer;
protected TcpMessageMapper mapper;
protected TcpListener listener;
private TcpListener actualListener;
private volatile Serializer serializer;
protected TcpSender sender;
private volatile TcpMessageMapper mapper;
protected boolean singleUse;
private volatile TcpListener listener;
protected final boolean server;
private volatile TcpListener actualListener;
protected String connectionId;
private AtomicLong sequence = new AtomicLong();
private int soLinger = -1;
private volatile TcpSender sender;
private String hostName = "unknown";
private volatile boolean singleUse;
private String hostAddress = "unknown";
private int port;
private final boolean lookupHost;
private final boolean server;
private volatile String connectionId;
private final AtomicLong sequence = new AtomicLong();
private volatile int soLinger = -1;
private volatile String hostName = "unknown";
private volatile String hostAddress = "unknown";
private volatile int port;
private int hashCode;
public AbstractTcpConnection(Socket socket, boolean server, boolean lookupHost) {
this.server = server;
this.lookupHost = lookupHost;
this.hashCode = socket.hashCode();
InetAddress inetAddress = socket.getInetAddress();
if (inetAddress != null) {
this.hostAddress = inetAddress.getHostAddress();
if (this.lookupHost) {
if (lookupHost) {
this.hostName = inetAddress.getHostName();
} else {
this.hostName = this.hostAddress;
}
}
this.connectionId = this.hostName + ":" + this.port + ":" + this.hashCode;
this.connectionId = this.hostName + ":" + this.port + ":" + UUID.randomUUID().toString();
try {
this.soLinger = socket.getSoLinger();
} catch (SocketException e) { }
}
public void afterSend(Message<?> message) throws Exception {
if (logger.isDebugEnabled())
logger.debug("Message sent " + message);
@@ -119,7 +114,7 @@ public abstract class AbstractTcpConnection implements TcpConnection {
}
/**
* If we have been intercepted, propagate the close from the outermost interceptor;
* If we have been intercepted, propagate the close from the outermost interceptor;
* otherwise, just call close().
*/
protected void closeConnection() {
@@ -147,14 +142,14 @@ public abstract class AbstractTcpConnection implements TcpConnection {
public void setMapper(TcpMessageMapper mapper) {
Assert.notNull(mapper, this.getClass().getName() + " Mapper may not be null");
this.mapper = mapper;
if (this.serializer != null &&
if (this.serializer != null &&
!(this.serializer instanceof AbstractByteArraySerializer)) {
mapper.setStringToBytes(false);
}
}
/**
*
*
* @return the deserializer
*/
public Deserializer<?> getDeserializer() {
@@ -169,7 +164,7 @@ public abstract class AbstractTcpConnection implements TcpConnection {
}
/**
*
*
* @return the serializer
*/
public Serializer<?> getSerializer() {
@@ -177,7 +172,7 @@ public abstract class AbstractTcpConnection implements TcpConnection {
}
/**
* @param serializer the serializer to set
* @param serializer the serializer to set
*/
public void setSerializer(Serializer<?> serializer) {
this.serializer = serializer;
@@ -202,7 +197,7 @@ public abstract class AbstractTcpConnection implements TcpConnection {
this.actualListener = outerInterceptor.getListener();
}
}
/**
* @param sender the sender to set
*/
@@ -219,9 +214,16 @@ public abstract class AbstractTcpConnection implements TcpConnection {
public TcpListener getListener() {
return this.listener;
}
/**
* @param singleUse true if this socket is to used once and
* @return the sender
*/
public TcpSender getSender() {
return sender;
}
/**
* @param singleUse true if this socket is to used once and
* discarded.
*/
public void setSingleUse(boolean singleUse) {
@@ -229,7 +231,7 @@ public abstract class AbstractTcpConnection implements TcpConnection {
}
/**
*
*
* @return True if connection is used once.
*/
public boolean isSingleUse() {
@@ -240,8 +242,8 @@ public abstract class AbstractTcpConnection implements TcpConnection {
return server;
}
public long getConnectionSeq() {
return sequence.incrementAndGet();
public long incrementAndGetConnectionSequence() {
return this.sequence.incrementAndGet();
}
public String getHostAddress() {
@@ -255,5 +257,5 @@ public abstract class AbstractTcpConnection implements TcpConnection {
public String getConnectionId() {
return this.connectionId;
}
}

View File

@@ -157,8 +157,8 @@ public abstract class AbstractTcpConnectionInterceptor implements TcpConnectionI
}
}
public long getConnectionSeq() {
return this.theConnection.getConnectionSeq();
public long incrementAndGetConnectionSequence() {
return this.theConnection.incrementAndGetConnectionSequence();
}
TcpSender getSender() {
@@ -176,5 +176,5 @@ public abstract class AbstractTcpConnectionInterceptor implements TcpConnectionI
this.realSender = sender != null;
return this.realSender;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2001-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
package org.springframework.integration.ip.tcp.connection;
import org.springframework.context.SmartLifecycle;
/**
@@ -25,8 +27,8 @@ package org.springframework.integration.ip.tcp.connection;
* @since 2.0
*
*/
public interface ConnectionFactory {
public interface ConnectionFactory extends SmartLifecycle {
public TcpConnection getConnection() throws Exception;
TcpConnection getConnection() throws Exception;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2001-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,9 +26,9 @@ import org.springframework.integration.Message;
/**
* An abstraction over {@link Socket} and {@link SocketChannel} that
* sends {@link Message} objects by serializing the payload
* and streaming it to the destination. Requires a {@link TcpListener}
* and streaming it to the destination. Requires a {@link TcpListener}
* to receive incoming messages.
*
*
* @author Gary Russell
* @since 2.0
*
@@ -38,115 +38,115 @@ public interface TcpConnection extends Runnable {
/**
* Closes the connection.
*/
public void close();
void close();
/**
* @return true if the connection is open.
*/
public boolean isOpen();
boolean isOpen();
/**
* Converts and sends the message.
* @param message The message
* @throws Exception
* @throws Exception
*/
public void send(Message<?> message) throws Exception;
void send(Message<?> message) throws Exception;
/**
* Uses the deserializer to obtain the message payload
* from the connection's input stream.
* @return The payload
* @throws Exception
* @throws Exception
*/
public Object getPayload() throws Exception;
Object getPayload() throws Exception;
/**
* @return the host name
*/
public String getHostName();
String getHostName();
/**
* @return the host address
*/
public String getHostAddress();
String getHostAddress();
/**
* @return the port
*/
public int getPort();
int getPort();
/**
* Sets the listener that will receive incoming Messages.
* Sets the listener that will receive incoming Messages.
* @param listener The listener
*/
public void registerListener(TcpListener listener);
void registerListener(TcpListener listener);
/**
* Registers a sender. Used on server side sockets so a
* sender can determine which connection to send a reply
* to.
* @param sender the sender
* @param sender the sender
*/
public void registerSender(TcpSender sender);
void registerSender(TcpSender sender);
/**
* @return a string uniquely representing a connection.
*/
public String getConnectionId();
String getConnectionId();
/**
* When true, the socket is used once and discarded.
* @param singleUse the singleUse
*/
public void setSingleUse(boolean singleUse);
void setSingleUse(boolean singleUse);
/**
*
*
* @return True if connection is used once.
*/
public boolean isSingleUse();
boolean isSingleUse();
/**
*
*
* @return True if connection is used once.
*/
public boolean isServer();
boolean isServer();
/**
* @param mapper the mapper
*/
public void setMapper(TcpMessageMapper mapper);
void setMapper(TcpMessageMapper mapper);
/**
*
*
* @return the deserializer
*/
public Deserializer<?> getDeserializer();
Deserializer<?> getDeserializer();
/**
* @param deserializer the deserializer to set
*/
public void setDeserializer(Deserializer<?> deserializer);
void setDeserializer(Deserializer<?> deserializer);
/**
*
*
* @return the serializer
*/
public Serializer<?> getSerializer();
Serializer<?> getSerializer();
/**
* @param serializer the serializer to set
*/
public void setSerializer(Serializer<?> serializer);
void setSerializer(Serializer<?> serializer);
/**
* @return this connection's listener
*/
public TcpListener getListener();
TcpListener getListener();
/**
* @return the next sequence number for a message received on this socket
*/
public long getConnectionSeq();
long incrementAndGetConnectionSequence();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2001-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,6 @@ package org.springframework.integration.ip.tcp.connection;
*/
public interface TcpConnectionInterceptor extends TcpConnection, TcpListener, TcpSender {
public void setTheConnection(TcpConnection connection);
void setTheConnection(TcpConnection connection);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2001-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,6 @@ public interface TcpConnectionInterceptorFactory {
*
* @return the TcpInterceptor
*/
public abstract TcpConnectionInterceptor getInterceptor();
abstract TcpConnectionInterceptor getInterceptor();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2001-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,6 @@ public interface TcpListener {
* @param message The message.
* @return true if the message was intercepted
*/
public abstract boolean onMessage(Message<?> message);
abstract boolean onMessage(Message<?> message);
}

View File

@@ -41,20 +41,35 @@ public class TcpMessageMapper implements
OutboundMessageMapper<Object> {
private volatile String charset = "UTF-8";
private volatile boolean stringToBytes = true;
private volatile boolean applySequence = false;
@SuppressWarnings("deprecation")
public Message<Object> toMessage(TcpConnection connection) throws Exception {
Message<Object> message = null;
Object payload = connection.getPayload();
if (payload != null) {
message = MessageBuilder.withPayload(payload)
.setHeader(IpHeaders.HOSTNAME, connection.getHostName())
.setHeader(IpHeaders.IP_ADDRESS, connection.getHostAddress())
.setHeader(IpHeaders.REMOTE_PORT, connection.getPort())
.setHeader(IpHeaders.CONNECTION_ID, connection.getConnectionId())
.setHeader(IpHeaders.CONNECTION_SEQ, connection.getConnectionSeq())
.build();
String connectionId = connection.getConnectionId();
if (this.applySequence) {
message = MessageBuilder.withPayload(payload)
.setHeader(IpHeaders.HOSTNAME, connection.getHostName())
.setHeader(IpHeaders.IP_ADDRESS, connection.getHostAddress())
.setHeader(IpHeaders.REMOTE_PORT, connection.getPort())
.setHeader(IpHeaders.CONNECTION_ID, connectionId)
.setCorrelationId(connectionId)
.setSequenceNumber((int) connection.incrementAndGetConnectionSequence())
.build();
} else {
message = MessageBuilder.withPayload(payload)
.setHeader(IpHeaders.HOSTNAME, connection.getHostName())
.setHeader(IpHeaders.IP_ADDRESS, connection.getHostAddress())
.setHeader(IpHeaders.REMOTE_PORT, connection.getPort())
.setHeader(IpHeaders.CONNECTION_ID, connectionId)
.setHeader(IpHeaders.CONNECTION_SEQ, connection.incrementAndGetConnectionSequence())
.build();
}
}
return message;
@@ -112,4 +127,11 @@ public class TcpMessageMapper implements
this.stringToBytes = stringToBytes;
}
/**
* @param applySequence the applySequence to set
*/
public void setApplySequence(boolean applySequence) {
this.applySequence = applySequence;
}
}

View File

@@ -45,18 +45,22 @@ public class TcpNetClientConnectionFactory extends
* reused for all requests while the connection remains open.
*/
public TcpConnection getConnection() throws Exception {
if (this.theConnection != null && this.theConnection.isOpen()) {
return this.theConnection;
this.checkActive();
TcpConnection theConnection = this.getTheConnection();
if (theConnection != null && theConnection.isOpen()) {
return theConnection;
}
logger.debug("Opening new socket connection to " + this.host + ":" + this.port);
Socket socket = createSocket(this.host, this.port);
if (logger.isDebugEnabled()) {
logger.debug("Opening new socket connection to " + this.getHost() + ":" + this.getPort());
}
Socket socket = createSocket(this.getHost(), this.getPort());
setSocketAttributes(socket);
TcpConnection connection = new TcpNetConnection(socket, false, this.isLookupHost());
connection = wrapConnection(connection);
initializeConnection(connection, socket);
this.getTaskExecutor().execute(connection);
if (!this.singleUse) {
this.theConnection = connection;
if (!this.isSingleUse()) {
this.setTheConnection(connection);
}
this.harvestClosedConnections();
return connection;
@@ -81,8 +85,4 @@ public class TcpNetClientConnectionFactory extends
public void run() {
}
public boolean isRunning() {
return this.active;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2001-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.springframework.integration.ip.tcp.connection;
import java.net.Socket;
import java.net.SocketTimeoutException;
import org.springframework.core.serializer.Serializer;
import org.springframework.core.serializer.Deserializer;
import org.springframework.integration.Message;
import org.springframework.integration.ip.tcp.serializer.SoftEndOfStreamException;
@@ -64,13 +65,13 @@ public class TcpNetConnection extends AbstractTcpConnection {
@SuppressWarnings("unchecked")
public synchronized void send(Message<?> message) throws Exception {
Object object = mapper.fromMessage(message);
this.serializer.serialize(object, this.socket.getOutputStream());
Object object = this.getMapper().fromMessage(message);
((Serializer<Object>) this.getSerializer()).serialize(object, this.socket.getOutputStream());
this.afterSend(message);
}
public Object getPayload() throws Exception {
return this.deserializer.deserialize(this.socket.getInputStream());
return this.getDeserializer().deserialize(this.socket.getInputStream());
}
public int getPort() {
@@ -88,7 +89,9 @@ public class TcpNetConnection extends AbstractTcpConnection {
* a warning is logged.
*/
public void run() {
if (this.listener == null && !this.singleUse) {
boolean singleUse = this.isSingleUse();
TcpListener listener = this.getListener();
if (listener == null && !singleUse) {
logger.debug("TcpListener exiting - no listener and not single use");
return;
}
@@ -98,11 +101,11 @@ public class TcpNetConnection extends AbstractTcpConnection {
boolean intercepted = false;
while (okToRun) {
try {
message = this.mapper.toMessage(this);
message = this.getMapper().toMessage(this);
} catch (Exception e) {
this.closeConnection();
if (!(e instanceof SoftEndOfStreamException)) {
if (e instanceof SocketTimeoutException && this.singleUse) {
if (e instanceof SocketTimeoutException && singleUse) {
logger.debug("Closing single use socket after timeout");
} else {
if (this.noReadErrorOnClose) {
@@ -135,8 +138,8 @@ public class TcpNetConnection extends AbstractTcpConnection {
intercepted = listener.onMessage(message);
} catch (Exception e) {
if (e instanceof NoListenerException) {
if (this.singleUse) {
logger.debug("Closing single use socket after inbound message " + this.connectionId);
if (singleUse) {
logger.debug("Closing single use socket after inbound message " + this.getConnectionId());
this.closeConnection();
okToRun = false;
} else {
@@ -151,8 +154,8 @@ public class TcpNetConnection extends AbstractTcpConnection {
* side, and the data was not intercepted,
* or the server side has no outbound adapter registered
*/
if (this.singleUse && ((!this.server && !intercepted) || (this.server && this.sender == null))) {
logger.debug("Closing single use socket after inbound message " + this.connectionId);
if (singleUse && ((!this.isServer() && !intercepted) || (this.isServer() && this.getSender() == null))) {
logger.debug("Closing single use socket after inbound message " + this.getConnectionId());
this.closeConnection();
okToRun = false;
}

View File

@@ -33,7 +33,7 @@ import javax.net.ServerSocketFactory;
*/
public class TcpNetServerConnectionFactory extends AbstractServerConnectionFactory {
protected ServerSocket serverSocket;
private ServerSocket serverSocket;
/**
* Listens for incoming connections on the port.
@@ -52,20 +52,20 @@ public class TcpNetServerConnectionFactory extends AbstractServerConnectionFacto
*/
public void run() {
ServerSocket theServerSocket = null;
if (this.listener == null) {
if (this.getListener() == null) {
logger.info("No listener bound to server connection factory; will not read; exiting...");
return;
}
try {
if (this.localAddress == null) {
theServerSocket = createServerSocket(this.port, this.poolSize, null);
if (this.getLocalAddress() == null) {
theServerSocket = createServerSocket(this.getPort(), this.getPoolSize(), null);
} else {
InetAddress whichNic = InetAddress.getByName(this.localAddress);
theServerSocket = createServerSocket(this.port, this.poolSize, whichNic);
InetAddress whichNic = InetAddress.getByName(this.getLocalAddress());
theServerSocket = createServerSocket(this.getPort(), this.getPoolSize(), whichNic);
}
this.serverSocket = theServerSocket;
this.listening = true;
logger.info("Listening on port " + this.port);
this.setListening(true);
logger.info("Listening on port " + this.getPort());
while (true) {
final Socket socket = serverSocket.accept();
logger.debug("Accepted connection from " + socket.getInetAddress().getHostAddress());
@@ -77,14 +77,14 @@ public class TcpNetServerConnectionFactory extends AbstractServerConnectionFacto
this.harvestClosedConnections();
}
} catch (Exception e) {
this.listening = false;
this.setListening(false);
// don't log an error if we had a good socket once and now it's closed
if (e instanceof SocketException && theServerSocket != null) {
logger.warn("Server Socket closed");
} else if (this.active) {
} else if (this.isActive()) {
logger.error("Error on ServerSocket", e);
}
this.active = false;
this.setActive(false);
}
}
@@ -102,17 +102,13 @@ public class TcpNetServerConnectionFactory extends AbstractServerConnectionFacto
protected ServerSocket createServerSocket(int port, int backlog, InetAddress whichNic) throws IOException {
if (whichNic == null) {
return ServerSocketFactory.getDefault().createServerSocket(port,
Math.abs(poolSize));
Math.abs(backlog));
} else {
return ServerSocketFactory.getDefault().createServerSocket(port,
Math.abs(poolSize), whichNic);
Math.abs(backlog), whichNic);
}
}
public boolean isRunning() {
return this.active;
}
public void close() {
if (this.serverSocket == null) {
return;
@@ -122,6 +118,12 @@ public class TcpNetServerConnectionFactory extends AbstractServerConnectionFacto
} catch (IOException e) {}
this.serverSocket = null;
}
/**
* @return the serverSocket
*/
protected ServerSocket getServerSocket() {
return serverSocket;
}
}

View File

@@ -36,13 +36,13 @@ import java.util.concurrent.LinkedBlockingQueue;
public class TcpNioClientConnectionFactory extends
AbstractClientConnectionFactory {
protected boolean usingDirectBuffers;
private boolean usingDirectBuffers;
private Selector selector;
protected Map<SocketChannel, TcpNioConnection> connections = new ConcurrentHashMap<SocketChannel, TcpNioConnection>();
private Map<SocketChannel, TcpNioConnection> connections = new ConcurrentHashMap<SocketChannel, TcpNioConnection>();
protected BlockingQueue<SocketChannel> newChannels = new LinkedBlockingQueue<SocketChannel>();
private BlockingQueue<SocketChannel> newChannels = new LinkedBlockingQueue<SocketChannel>();
/**
@@ -60,6 +60,7 @@ public class TcpNioClientConnectionFactory extends
* reused for all requests while the connection remains open.
*/
public TcpConnection getConnection() throws Exception {
this.checkActive();
int n = 0;
while (this.selector == null) {
try {
@@ -71,11 +72,13 @@ public class TcpNioClientConnectionFactory extends
throw new Exception("Factory failed to start");
}
}
if (this.theConnection != null && this.theConnection.isOpen()) {
return this.theConnection;
if (this.getTheConnection() != null && this.getTheConnection().isOpen()) {
return this.getTheConnection();
}
logger.debug("Opening new socket channel connection to " + this.host + ":" + this.port);
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(this.host, this.port));
if (logger.isDebugEnabled()) {
logger.debug("Opening new socket channel connection to " + this.getHost() + ":" + this.getPort());
}
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(this.getHost(), this.getPort()));
setSocketAttributes(socketChannel.socket());
TcpNioConnection connection = new TcpNioConnection(socketChannel, false, this.isLookupHost());
connection.setUsingDirectBuffers(this.usingDirectBuffers);
@@ -83,14 +86,14 @@ public class TcpNioClientConnectionFactory extends
TcpConnection wrappedConnection = wrapConnection(connection);
initializeConnection(wrappedConnection, socketChannel.socket());
socketChannel.configureBlocking(false);
if (this.soTimeout > 0) {
if (this.getSoTimeout() > 0) {
connection.setLastRead(System.currentTimeMillis());
}
this.connections.put(socketChannel, connection);
newChannels.add(socketChannel);
selector.wakeup();
if (!this.singleUse) {
this.theConnection = wrappedConnection;
if (!this.isSingleUse()) {
this.setTheConnection(wrappedConnection);
}
return wrappedConnection;
}
@@ -112,12 +115,14 @@ public class TcpNioClientConnectionFactory extends
}
public void run() {
logger.debug("Read selector running for connections to " + host + ":" + port);
if (logger.isDebugEnabled()) {
logger.debug("Read selector running for connections to " + this.getHost() + ":" + this.getPort());
}
try {
this.selector = Selector.open();
while (this.active) {
while (this.isActive()) {
SocketChannel newChannel;
int selectionCount = selector.select(this.soTimeout);
int selectionCount = selector.select(this.getSoTimeout());
while ((newChannel = newChannels.poll()) != null) {
newChannel.register(this.selector, SelectionKey.OP_READ, connections.get(newChannel));
}
@@ -125,13 +130,32 @@ public class TcpNioClientConnectionFactory extends
}
} catch (Exception e) {
logger.error("Exception in read selector thread", e);
this.active = false;
this.setActive(false);
}
if (logger.isDebugEnabled()) {
logger.debug("Read selector exiting for connections to " + this.getHost() + ":" + this.getPort());
}
logger.debug("Read selector exiting for connections to " + host + ":" + port);
}
public boolean isRunning() {
return this.active;
/**
* @return the usingDirectBuffers
*/
protected boolean isUsingDirectBuffers() {
return usingDirectBuffers;
}
/**
* @return the connections
*/
protected Map<SocketChannel, TcpNioConnection> getConnections() {
return connections;
}
/**
* @return the newChannels
*/
protected BlockingQueue<SocketChannel> getNewChannels() {
return newChannels;
}
}

View File

@@ -30,6 +30,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.core.serializer.Serializer;
import org.springframework.integration.Message;
import org.springframework.integration.ip.tcp.serializer.SoftEndOfStreamException;
@@ -43,27 +44,27 @@ import org.springframework.integration.ip.tcp.serializer.SoftEndOfStreamExceptio
public class TcpNioConnection extends AbstractTcpConnection {
private final SocketChannel socketChannel;
private volatile OutputStream channelOutputStream;
private volatile PipedOutputStream pipedOutputStream;
private volatile PipedInputStream pipedInputStream;
private volatile boolean usingDirectBuffers;
private volatile Executor taskExecutor;
private volatile ByteBuffer rawBuffer;
private volatile int maxMessageSize = 60 * 1024;
private volatile long lastRead;
private AtomicInteger executionControl = new AtomicInteger();
private volatile boolean writingToPipe;
/**
* Constructs a TcpNetConnection for the SocketChannel.
* @param socketChannel the socketChannel
@@ -77,7 +78,7 @@ public class TcpNioConnection extends AbstractTcpConnection {
this.pipedOutputStream = new PipedOutputStream(this.pipedInputStream);
this.channelOutputStream = new ChannelOutputStream();
}
public void close() {
doClose();
}
@@ -100,21 +101,21 @@ public class TcpNioConnection extends AbstractTcpConnection {
@SuppressWarnings("unchecked")
public void send(Message<?> message) throws Exception {
synchronized(mapper) {
Object object = mapper.fromMessage(message);
this.serializer.serialize(object, this.channelOutputStream);
synchronized(this.getMapper()) {
Object object = this.getMapper().fromMessage(message);
((Serializer<Object>) this.getSerializer()).serialize(object, this.channelOutputStream);
this.afterSend(message);
}
}
public Object getPayload() throws Exception {
return this.deserializer.deserialize(pipedInputStream);
return this.getDeserializer().deserialize(pipedInputStream);
}
public int getPort() {
return this.socketChannel.socket().getPort();
}
/**
* Allocates a ByteBuffer of the requested length using normal or
* direct buffers, depending on the usingDirectBuffers field.
@@ -128,7 +129,7 @@ public class TcpNioConnection extends AbstractTcpConnection {
}
return buffer;
}
/**
* If there is no listener, and this connection is not for single use,
* this method exits. When there is a listener, this method assembles
@@ -142,8 +143,8 @@ public class TcpNioConnection extends AbstractTcpConnection {
logger.trace(this.getConnectionId() + " Nio message assembler running...");
}
try {
if (this.listener == null && !this.singleUse) {
logger.debug("TcpListener exiting - no listener and not single use");
if (this.getListener() == null && !this.isSingleUse()) {
logger.debug("TcpListener exiting - no listener and not single use");
return;
}
try {
@@ -206,12 +207,12 @@ public class TcpNioConnection extends AbstractTcpConnection {
}
Message<?> message = null;
try {
message = this.mapper.toMessage(this);
message = this.getMapper().toMessage(this);
} catch (Exception e) {
this.closeConnection();
if (e instanceof SocketTimeoutException && this.singleUse) {
if (e instanceof SocketTimeoutException && this.isSingleUse()) {
if (logger.isDebugEnabled()) {
logger.debug("Closing single use socket after timeout " + this.connectionId);
logger.debug("Closing single use socket after timeout " + this.getConnectionId());
}
} else {
if (!(e instanceof SoftEndOfStreamException)) {
@@ -219,7 +220,7 @@ public class TcpNioConnection extends AbstractTcpConnection {
}
}
return null;
}
}
return message;
}
@@ -227,13 +228,13 @@ public class TcpNioConnection extends AbstractTcpConnection {
boolean intercepted = false;
try {
if (message != null) {
intercepted = listener.onMessage(message);
intercepted = getListener().onMessage(message);
}
} catch (Exception e) {
if (e instanceof NoListenerException) {
if (this.singleUse) {
if (this.isSingleUse()) {
if (logger.isDebugEnabled()) {
logger.debug("Closing single use channel after inbound message " + this.connectionId);
logger.debug("Closing single use channel after inbound message " + this.getConnectionId());
}
this.closeConnection();
}
@@ -246,12 +247,14 @@ public class TcpNioConnection extends AbstractTcpConnection {
* side, and the data was not intercepted,
* or the server side has no outbound adapter registered
*/
if (this.singleUse && ((!this.server && !intercepted) || (this.server && this.sender == null))) {
logger.debug("Closing single use cbannel after inbound message " + this.connectionId);
if (this.isSingleUse() && ((!this.isServer() && !intercepted) || (this.isServer() && this.getSender() == null))) {
if (logger.isDebugEnabled()) {
logger.debug("Closing single use cbannel after inbound message " + this.getConnectionId());
}
this.closeConnection();
}
}
private void doRead() throws Exception {
if (this.rawBuffer == null) {
this.rawBuffer = allocate(maxMessageSize);
@@ -311,14 +314,14 @@ public class TcpNioConnection extends AbstractTcpConnection {
this.closeConnection();
}
}
/**
* Close the socket due to timeout.
*/
void timeout() {
this.closeConnection();
}
/**
*
* @param taskExecutor the taskExecutor to set
@@ -326,7 +329,7 @@ public class TcpNioConnection extends AbstractTcpConnection {
public void setTaskExecutor(Executor taskExecutor) {
this.taskExecutor = taskExecutor;
}
/**
* If true, connection will attempt to use direct buffers where
* possible.
@@ -359,9 +362,9 @@ public class TcpNioConnection extends AbstractTcpConnection {
class ChannelOutputStream extends OutputStream {
private Selector selector;
private int soTimeout;
@Override
public void write(int b) throws IOException {
byte[] bytes = new byte[1];
@@ -390,7 +393,7 @@ public class TcpNioConnection extends AbstractTcpConnection {
ByteBuffer buffer = ByteBuffer.wrap(b);
doWrite(buffer);
}
private synchronized void doWrite(ByteBuffer buffer) throws IOException {
socketChannel.write(buffer);
int remaining = buffer.remaining();
@@ -414,5 +417,5 @@ public class TcpNioConnection extends AbstractTcpConnection {
}
}
}

View File

@@ -39,11 +39,11 @@ import java.util.Map;
*/
public class TcpNioServerConnectionFactory extends AbstractServerConnectionFactory {
protected ServerSocketChannel serverChannel;
private ServerSocketChannel serverChannel;
protected boolean usingDirectBuffers;
private boolean usingDirectBuffers;
protected Map<SocketChannel, TcpNioConnection> connections = new HashMap<SocketChannel, TcpNioConnection>();
private Map<SocketChannel, TcpNioConnection> connections = new HashMap<SocketChannel, TcpNioConnection>();
private Selector selector;
@@ -63,34 +63,35 @@ public class TcpNioServerConnectionFactory extends AbstractServerConnectionFacto
* I/O errors on the server socket/channel are logged and the factory is stopped.
*/
public void run() {
if (this.listener == null) {
if (this.getListener() == null) {
logger.info("No listener bound to server connection factory; will not read; exiting...");
return;
}
try {
this.serverChannel = ServerSocketChannel.open();
logger.info("Listening on port " + this.port);
int port = this.getPort();
logger.info("Listening on port " + port);
this.serverChannel.configureBlocking(false);
if (this.localAddress == null) {
this.serverChannel.socket().bind(new InetSocketAddress(this.port),
Math.abs(this.poolSize));
if (this.getLocalAddress() == null) {
this.serverChannel.socket().bind(new InetSocketAddress(port),
Math.abs(this.getPoolSize()));
} else {
InetAddress whichNic = InetAddress.getByName(this.localAddress);
this.serverChannel.socket().bind(new InetSocketAddress(whichNic, this.port),
Math.abs(this.poolSize));
InetAddress whichNic = InetAddress.getByName(this.getLocalAddress());
this.serverChannel.socket().bind(new InetSocketAddress(whichNic, port),
Math.abs(this.getPoolSize()));
}
final Selector selector = Selector.open();
this.serverChannel.register(selector, SelectionKey.OP_ACCEPT);
this.listening = true;
this.setListening(true);
this.selector = selector;
doSelect(this.serverChannel, selector);
} catch (IOException e) {
this.close();
this.listening = false;
if (this.active) {
this.setListening(false);
if (this.isActive()) {
logger.error("Error on ServerSocketChannel", e);
this.active = false;
this.setActive(false);
}
}
}
@@ -111,8 +112,8 @@ public class TcpNioServerConnectionFactory extends AbstractServerConnectionFacto
*/
private void doSelect(ServerSocketChannel server, final Selector selector)
throws IOException, ClosedChannelException, SocketException {
while (this.active) {
int selectionCount = selector.select(this.soTimeout);
while (this.isActive()) {
int selectionCount = selector.select(this.getSoTimeout());
this.processNioSelections(selectionCount, selector, server, this.connections);
}
}
@@ -155,10 +156,6 @@ public class TcpNioServerConnectionFactory extends AbstractServerConnectionFacto
}
}
public boolean isRunning() {
return this.active;
}
public void close() {
if (this.selector != null) {
this.selector.wakeup();
@@ -175,6 +172,27 @@ public class TcpNioServerConnectionFactory extends AbstractServerConnectionFacto
public void setUsingDirectBuffers(boolean usingDirectBuffers) {
this.usingDirectBuffers = usingDirectBuffers;
}
/**
* @return the serverChannel
*/
protected ServerSocketChannel getServerChannel() {
return serverChannel;
}
/**
* @return the usingDirectBuffers
*/
protected boolean isUsingDirectBuffers() {
return usingDirectBuffers;
}
/**
* @return the connections
*/
protected Map<SocketChannel, TcpNioConnection> getConnections() {
return connections;
}
}

View File

@@ -37,7 +37,7 @@ public abstract class AbstractByteArraySerializer implements
protected int maxMessageSize = 2048;
protected Log logger = LogFactory.getLog(this.getClass());
protected final Log logger = LogFactory.getLog(this.getClass());
/**
* The maximum supported message size for this serializer.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ import org.springframework.integration.MessagingException;
*/
public class MulticastReceivingChannelAdapter extends UnicastReceivingChannelAdapter {
protected String group;
private String group;
/**
@@ -61,25 +61,23 @@ public class MulticastReceivingChannelAdapter extends UnicastReceivingChannelAda
@Override
protected synchronized DatagramSocket getSocket() {
if (this.socket == null) {
if (this.getTheSocket() == null) {
try {
MulticastSocket socket = new MulticastSocket(this.port);
MulticastSocket socket = new MulticastSocket(this.getPort());
String localAddress = this.getLocalAddress();
if (localAddress != null) {
InetAddress whichNic = InetAddress.getByName(this.localAddress);
InetAddress whichNic = InetAddress.getByName(localAddress);
socket.setInterface(whichNic);
}
socket.setSoTimeout(this.soTimeout);
if (this.soReceiveBufferSize > 0) {
socket.setReceiveBufferSize(this.soReceiveBufferSize);
}
this.setSocketAttributes(socket);
socket.joinGroup(InetAddress.getByName(this.group));
this.socket = socket;
this.setSocket(socket);
}
catch (IOException e) {
throw new MessagingException("failed to create DatagramSocket", e);
}
}
return this.socket;
return super.getSocket();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2001-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,9 +39,9 @@ import org.springframework.integration.core.MessageHandler;
*/
public class MulticastSendingMessageHandler extends UnicastSendingMessageHandler {
protected int timeToLive = -1;
private int timeToLive = -1;
protected String localAddress;
private String localAddress;
/**
* Constructs a MulticastSendingMessageHandler to send data to the multicast address/port.
@@ -99,20 +99,20 @@ public class MulticastSendingMessageHandler extends UnicastSendingMessageHandler
@Override
protected synchronized DatagramSocket getSocket() throws IOException {
if (this.socket == null) {
if (this.getTheSocket() == null) {
MulticastSocket socket;
if (acknowledge) {
if (this.isAcknowledge()) {
if (logger.isDebugEnabled()) {
logger.debug("Listening for acks on port: " + ackPort);
logger.debug("Listening for acks on port: " + this.getAckPort());
}
if (localAddress == null) {
socket = new MulticastSocket(this.ackPort);
socket = new MulticastSocket(this.getAckPort());
} else {
InetAddress whichNic = InetAddress.getByName(this.localAddress);
socket = new MulticastSocket(new InetSocketAddress(whichNic, this.ackPort));
socket = new MulticastSocket(new InetSocketAddress(whichNic, this.getAckPort()));
}
if (this.soReceiveBufferSize > 0) {
socket.setReceiveBufferSize(this.soReceiveBufferSize);
if (this.getSoReceiveBufferSize() > 0) {
socket.setReceiveBufferSize(this.getSoReceiveBufferSize());
}
} else {
socket = new MulticastSocket();
@@ -126,9 +126,9 @@ public class MulticastSendingMessageHandler extends UnicastSendingMessageHandler
NetworkInterface intfce = NetworkInterface.getByInetAddress(whichNic);
socket.setNetworkInterface(intfce);
}
this.socket = socket;
this.setSocket(socket);
}
return this.socket;
return this.getSocket();
}
@@ -137,7 +137,7 @@ public class MulticastSendingMessageHandler extends UnicastSendingMessageHandler
* @param minAcksForSuccess
*/
public void setMinAcksForSuccess(int minAcksForSuccess) {
this.ackCounter = minAcksForSuccess;
this.setAckCounter(minAcksForSuccess);
}
/**

View File

@@ -33,20 +33,20 @@ import org.springframework.integration.ip.AbstractInternetProtocolReceivingChann
import org.springframework.integration.ip.IpHeaders;
/**
* A channel adapter to receive incoming UDP packets. Packets can optionally be preceded by a
* A channel adapter to receive incoming UDP packets. Packets can optionally be preceded by a
* 4 byte length field, used to validate that all data was received. Packets may also contain
* information indicating an acknowledgment needs to be sent.
*
*
* @author Gary Russell
* @since 2.0
*/
public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolReceivingChannelAdapter {
protected volatile DatagramSocket socket;
private volatile DatagramSocket socket;
protected final DatagramPacketMessageMapper mapper = new DatagramPacketMessageMapper();
private final DatagramPacketMessageMapper mapper = new DatagramPacketMessageMapper();
protected volatile int soSendBufferSize = -1;
private volatile int soSendBufferSize = -1;
private static Pattern addressPattern = Pattern.compile("([^:]*):([0-9]*)");
@@ -75,15 +75,15 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
public void run() {
if (logger.isDebugEnabled()) {
logger.debug("UDP Receiver running on port:" + port);
logger.debug("UDP Receiver running on port:" + this.getPort());
}
checkTaskExecutor("UDP-Incoming-Msg-Handler");
listening = true;
this.setListening(true);
// Do as little as possible here so we can loop around and catch the next packet.
// Just schedule the packet for processing.
while (this.active) {
while (this.isActive()) {
try {
asyncSendMessage(receive());
}
@@ -91,7 +91,7 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
// continue
}
catch (SocketException e) {
doStop();
doStop();
}
catch (Exception e) {
if (e instanceof MessagingException) {
@@ -100,7 +100,7 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
throw new MessagingException("failed to receive DatagramPacket", e);
}
}
listening = false;
this.setListening(false);
}
protected void sendAck(Message<byte[]> message) {
@@ -133,7 +133,7 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
}
protected boolean asyncSendMessage(final DatagramPacket packet) {
this.taskExecutor.execute(new Runnable(){
this.getTaskExecutor().execute(new Runnable(){
public void run() {
Message<byte[]> message = null;
try {
@@ -156,26 +156,36 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
protected DatagramPacket receive() throws Exception {
DatagramSocket socket = this.getSocket();
final byte[] buffer = new byte[this.receiveBufferSize];
final byte[] buffer = new byte[this.getReceiveBufferSize()];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
return packet;
}
/**
* @param socket the socket to set
*/
public void setSocket(DatagramSocket socket) {
this.socket = socket;
}
protected DatagramSocket getTheSocket() {
return this.socket;
}
protected synchronized DatagramSocket getSocket() {
if (this.socket == null) {
try {
DatagramSocket socket = null;
String localAddress = this.getLocalAddress();
if (localAddress == null) {
this.socket = new DatagramSocket(this.port);
socket = new DatagramSocket(this.getPort());
} else {
InetAddress whichNic = InetAddress.getByName(this.localAddress);
this.socket = new DatagramSocket(this.port, whichNic);
}
this.socket.setSoTimeout(this.soTimeout);
if (this.soReceiveBufferSize > 0) {
this.socket.setReceiveBufferSize(this.soReceiveBufferSize);
InetAddress whichNic = InetAddress.getByName(localAddress);
socket = new DatagramSocket(this.getPort(), whichNic);
}
setSocketAttributes(socket);
this.socket = socket;
}
catch (IOException e) {
throw new MessagingException("failed to create DatagramSocket", e);
@@ -184,12 +194,28 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
return this.socket;
}
/**
* Sets timeout and receive buffer size
*
* @param socket
* @throws SocketException
*/
protected void setSocketAttributes(DatagramSocket socket)
throws SocketException {
socket.setSoTimeout(this.getSoTimeout());
int soReceiveBufferSize = this.getSoReceiveBufferSize();
if (soReceiveBufferSize > 0) {
socket.setReceiveBufferSize(soReceiveBufferSize);
}
}
@Override
protected void doStop() {
super.doStop();
try {
this.socket.close();
DatagramSocket socket = this.socket;
this.socket = null;
socket.close();
}
catch (Exception e) {
// ignore
@@ -199,11 +225,11 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
public void setSoSendBufferSize(int soSendBufferSize) {
this.soSendBufferSize = soSendBufferSize;
}
public void setLookupHost(boolean lookupHost) {
this.mapper.setLookupHost(lookupHost);
}
public String getComponentType(){
return "ip:udp-inbound-channel-adapter";
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2001-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.Collections;
import java.util.HashMap;
@@ -37,12 +36,11 @@ import org.springframework.integration.MessageDeliveryException;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.MessageRejectedException;
import org.springframework.integration.MessagingException;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.ip.AbstractInternetProtocolSendingMessageHandler;
import org.springframework.util.Assert;
/**
* A {@link MessageHandler} implementation that maps a Message into
* A {@link org.springframework.integration.core.MessageHandler} implementation that maps a Message into
* a UDP datagram packet and sends that to the specified host and port.
*
* Messages can be basic, with no support for reliability, can be prefixed
@@ -55,39 +53,39 @@ import org.springframework.util.Assert;
public class UnicastSendingMessageHandler extends
AbstractInternetProtocolSendingMessageHandler implements Runnable{
protected final DatagramPacketMessageMapper mapper = new DatagramPacketMessageMapper();
private final DatagramPacketMessageMapper mapper = new DatagramPacketMessageMapper();
protected volatile DatagramSocket socket;
private volatile DatagramSocket socket;
/**
* If true adds headers to instruct receiving adapter to return an ack.
*/
protected volatile boolean waitForAck = false;
protected volatile boolean acknowledge = false;
private volatile boolean waitForAck = false;
protected volatile int ackPort;
private volatile boolean acknowledge = false;
protected volatile int ackTimeout = 5000;
private volatile int ackPort;
protected volatile int ackCounter = 1;
private volatile int ackTimeout = 5000;
protected volatile Map<String, CountDownLatch> ackControl = Collections
private volatile int ackCounter = 1;
private volatile Map<String, CountDownLatch> ackControl = Collections
.synchronizedMap(new HashMap<String, CountDownLatch>());
protected volatile Exception fatalException;
private volatile Exception fatalException;
protected int soReceiveBufferSize = -1;
private volatile int soReceiveBufferSize = -1;
protected String localAddress;
private CountDownLatch ackLatch;
private volatile String localAddress;
private boolean ackThreadRunning;
private volatile CountDownLatch ackLatch;
private volatile boolean ackThreadRunning;
private volatile Executor taskExecutor;
protected volatile Executor taskExecutor;
/**
* Basic constructor; no reliability; no acknowledgment.
* @param host Destination host.
@@ -153,7 +151,7 @@ public class UnicastSendingMessageHandler extends
ackTimeout);
}
protected void setReliabilityAttributes(boolean lengthCheck,
protected final void setReliabilityAttributes(boolean lengthCheck,
boolean acknowledge, String ackHost, int ackPort, int ackTimeout) {
this.mapper.setLengthCheck(lengthCheck);
this.waitForAck = acknowledge;
@@ -248,10 +246,18 @@ public class UnicastSendingMessageHandler extends
protected void send(DatagramPacket packet) throws Exception {
DatagramSocket socket = this.getSocket();
packet.setSocketAddress(this.destinationAddress);
packet.setSocketAddress(this.getDestinationAddress());
socket.send(packet);
}
protected void setSocket(DatagramSocket socket) {
this.socket = socket;
}
protected DatagramSocket getTheSocket() {
return this.socket;
}
protected synchronized DatagramSocket getSocket() throws IOException {
if (this.socket == null) {
if (acknowledge) {
@@ -276,11 +282,11 @@ public class UnicastSendingMessageHandler extends
}
protected void setSocketAttributes(DatagramSocket socket) throws SocketException {
if (this.soTimeout >= 0) {
socket.setSoTimeout(this.soTimeout);
if (this.getSoTimeout() >= 0) {
socket.setSoTimeout(this.getSoTimeout());
}
if (this.soSendBufferSize > 0) {
socket.setSendBufferSize(this.soSendBufferSize);
if (this.getSoSendBufferSize() > 0) {
socket.setSendBufferSize(this.getSoSendBufferSize());
}
}
@@ -334,7 +340,7 @@ public class UnicastSendingMessageHandler extends
}
/**
* @see Socket#setReceiveBufferSize(int)
* @see java.net.Socket#setReceiveBufferSize(int)
* @see DatagramSocket#setReceiveBufferSize(int)
*/
public void setSoReceiveBufferSize(int size) {
@@ -348,8 +354,36 @@ public class UnicastSendingMessageHandler extends
public void setTaskExecutor(Executor taskExecutor) {
this.taskExecutor = taskExecutor;
}
/**
* @param ackCounter the ackCounter to set
*/
public void setAckCounter(int ackCounter) {
this.ackCounter = ackCounter;
}
public String getComponentType(){
return "ip:udp-outbound-channel-adapter";
}
/**
* @return the acknowledge
*/
public boolean isAcknowledge() {
return acknowledge;
}
/**
* @return the ackPort
*/
public int getAckPort() {
return ackPort;
}
/**
* @return the soReceiveBufferSize
*/
public int getSoReceiveBufferSize() {
return soReceiveBufferSize;
}
}

View File

@@ -65,7 +65,7 @@ its configuration specifies the number of threads.
<xsd:attribute name="lookup-host" type="xsd:string" >
<xsd:annotation>
<xsd:documentation>
Whether or not to do a DNS reverse-lookup on the remote ip address to insert the host name into the
Whether or not to do a DNS reverse-lookup on the remote ip address to insert the host name into the
message headers (ip_hostName). Default "true".
</xsd:documentation>
</xsd:annotation>
@@ -115,183 +115,199 @@ task executors such as a WorkManagerTaskExecutor.
<xsd:element name="tcp-inbound-channel-adapter">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="connection-factory" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.ip.tcp.connection.ConnectionFactory"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
A connection factory is needed by an inbound adapter. If the connection factory has a type 'server',
the factory is 'owned' by this adapter. If it has a type 'client', it is owned by an outbound channel
adapter and this adapter will receive any incoming messages on the connection created by the outbound
adapter.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="error-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
If a (synchronous) downstream exception is thrown and an "error-channel" is specified,
the MessagingException will be sent to this channel. Otherwise, any such exception
will simply be logged by the channel adapter.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:complexContent>
<xsd:extension base="smartLifeCycleType">
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="connection-factory" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.ip.tcp.connection.ConnectionFactory"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
A connection factory is needed by an inbound adapter. If the connection factory has a type 'server',
the factory is 'owned' by this adapter. If it has a type 'client', it is owned by an outbound channel
adapter and this adapter will receive any incoming messages on the connection created by the outbound
adapter.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="error-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
If a (synchronous) downstream exception is thrown and an "error-channel" is specified,
the MessagingException will be sent to this channel. Otherwise, any such exception
will simply be logged by the channel adapter.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="tcp-outbound-channel-adapter">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="connection-factory" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.ip.tcp.connection.ConnectionFactory"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
A connection factory is needed by an outbound adapter. If the connection factory has a type 'client',
the factory is 'owned' by this adapter. If it has a type 'server', it is owned by an inbound channel
adapter and this adapter will attempt to correlate messages to the connection on which an original
inbound message was received.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order">
<xsd:annotation>
<xsd:documentation>
Specifies the order for invocation when this endpoint is connected as a
subscriber to a SubscribableChannel.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:complexContent>
<xsd:extension base="smartLifeCycleType">
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="connection-factory" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.ip.tcp.connection.ConnectionFactory"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
A connection factory is needed by an outbound adapter. If the connection factory has a type 'client',
the factory is 'owned' by this adapter. If it has a type 'server', it is owned by an inbound channel
adapter and this adapter will attempt to correlate messages to the connection on which an original
inbound message was received.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order">
<xsd:annotation>
<xsd:documentation>
Specifies the order for invocation when this endpoint is connected as a
subscriber to a SubscribableChannel.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="tcp-inbound-gateway">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="connection-factory" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
A connection factory is needed by an inbound adapter. The connection factory must be of type 'server'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-channel" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-timeout" type="xsd:string"/>
<xsd:attribute name="error-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
If a (synchronous) downstream exception is thrown and an "error-channel" is specified,
the MessagingException will be sent to this channel and the ultimate response
of the error flow will be returned as a response by the gateway. If no
"error-channel" is specified, any such exception
will simply be logged by the gateway. In such a situation, no response is sent
to the client.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:complexContent>
<xsd:extension base="smartLifeCycleType">
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="connection-factory" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
A connection factory is needed by an inbound adapter. The connection factory must be of type 'server'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-channel" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-timeout" type="xsd:string"/>
<xsd:attribute name="error-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
If a (synchronous) downstream exception is thrown and an "error-channel" is specified,
the MessagingException will be sent to this channel and the ultimate response
of the error flow will be returned as a response by the gateway. If no
"error-channel" is specified, any such exception
will simply be logged by the gateway. In such a situation, no response is sent
to the client.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="tcp-outbound-gateway">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="connection-factory" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.ip.tcp.connection.ConnectionFactory"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
A connection factory is needed by an outbound adapter. The connection factory must be of 'client'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-channel" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-timeout" type="xsd:string"/>
<xsd:attribute name="reply-timeout" type="xsd:string"/>
<xsd:attribute name="order">
<xsd:annotation>
<xsd:documentation>
Specifies the order for invocation when this endpoint is connected as a
subscriber to a SubscribableChannel.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:complexContent>
<xsd:extension base="smartLifeCycleType">
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="connection-factory" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.ip.tcp.connection.ConnectionFactory"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
A connection factory is needed by an outbound adapter. The connection factory must be of 'client'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-channel" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-timeout" type="xsd:string"/>
<xsd:attribute name="reply-timeout" type="xsd:string"/>
<xsd:attribute name="order">
<xsd:annotation>
<xsd:documentation>
Specifies the order for invocation when this endpoint is connected as a
subscriber to a SubscribableChannel.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
@@ -317,7 +333,7 @@ connection request.
<xsd:attribute name="host" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The host to which a client connection factory will connect.
The host to which a client connection factory will connect.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -326,14 +342,14 @@ The host to which a client connection factory will connect.
<xsd:documentation>
For client factories, the port to which a client connection factory will connect.
For server factories, the port on which the factory will listen for incoming
connections.
connections.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="using-nio" type="xsd:string" default="false">
<xsd:annotation>
<xsd:documentation>
If true, the factory will use java.nio.channel.SocketChannel for communication;
If true, the factory will use java.nio.channel.SocketChannel for communication;
for a large number of connections on the server side, this can provide better
performance and may use fewer threads.
</xsd:documentation>
@@ -350,10 +366,10 @@ performance and may use fewer threads.
<xsd:annotation>
<xsd:documentation>
If true, instructs the factory to use direct buffers if possible; only applies if
using-nio is true. Refer to ByteBuffer javadocs for more information.
using-nio is true. Refer to ByteBuffer javadocs for more information.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attribute>
<xsd:attribute name="single-use" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
@@ -363,7 +379,7 @@ be closed after a message is received. For outbound adapters where there is
no inbound adapter sharing the factory, or for inbound adapters where an
outbound adapter shares the factory, the connection will be closed after
so-timeout milliseconds. For outbound adapters where an inbound adapter shares
the factory, the connection will be closed after a response is received.
the factory, the connection will be closed after a response is received.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -377,7 +393,7 @@ the factory, the connection will be closed after a response is received.
<xsd:documentation>
A Serializer that converts message payloads to/from output streams/input streams
associated with the connection. Default is ByteArrayCrLfSerializer. Serializer and Deserializer
would normally be the same but this is not required.
would normally be the same but this is not required.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -391,7 +407,7 @@ would normally be the same but this is not required.
<xsd:documentation>
A Deserializer that converts message payloads to/from output streams/input streams
associated with the connection. Default is ByteArrayCrLfSerializer. Serializer and Deserializer
would normally be the same but this is not required.
would normally be the same but this is not required.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -420,7 +436,7 @@ its configuration specifies the number of threads.
<xsd:attribute name="lookup-host" type="xsd:string" >
<xsd:annotation>
<xsd:documentation>
Whether or not to do a DNS reverse-lookup on the remote ip address to insert the host name into the
Whether or not to do a DNS reverse-lookup on the remote ip address to insert the host name into the
message headers (ip_connectionId, ip_hostName). Default "true".
</xsd:documentation>
</xsd:annotation>
@@ -434,6 +450,14 @@ message headers (ip_connectionId, ip_hostName). Default "true".
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="apply-sequence" type="xsd:string" default="false">
<xsd:annotation>
<xsd:documentation>
When set to "true", adds sequenceNumber and correlationId headers to messages originating from
connections created by this factory. Facilitates resequencing if necessary. Default "false".
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
@@ -480,4 +504,23 @@ apply to TCP outbound adapters and gateways.
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="smartLifeCycleType">
<xsd:attribute name="auto-startup" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Boolean value indicating whether this endpoint should start automatically.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="phase" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The lifecycle phase within which this endpoint should start and stop.
The lower the value the earlier this endpoint will start and the later it will stop. The
default is 0. Values can be negative. See SmartLifeCycle.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:schema>

View File

@@ -12,15 +12,16 @@
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="tcpIpUtils" class="org.springframework.integration.ip.util.SocketTestUtils" />
<int:channel id="udpChannel" />
<int:channel id="tcpChannel" />
<int:channel id="replyChannel" />
<task:executor id="externalTE" pool-size="10"/>
<ip:udp-inbound-channel-adapter id="testInUdp"
channel="udpChannel"
check-length="true"
@@ -48,22 +49,25 @@
so-receive-buffer-size="30"
so-send-buffer-size="31"
so-timeout="32"
local-address="127.0.0.1"
local-address="127.0.0.1"
/>
<ip:tcp-connection-factory id="cfS1"
type="server"
port="#{tcpIpUtils.findAvailableServerSocket(5200)}"
lookup-host="false"
apply-sequence="true"
/>
<ip:tcp-inbound-channel-adapter id="testInTcp"
channel="tcpChannel"
error-channel="errorChannel"
connection-factory="cfS1"
auto-startup="false"
phase="124"
/>
<ip:udp-outbound-channel-adapter id="testOutUdp"
<ip:udp-outbound-channel-adapter id="testOutUdp"
ack-host="somehost"
ack-port="#{tcpIpUtils.findAvailableUdpSocket(5300)}"
ack-timeout="51"
@@ -76,12 +80,12 @@
so-receive-buffer-size="52"
so-send-buffer-size="53"
so-timeout="54"
local-address="127.0.0.1"
local-address="127.0.0.1"
task-executor="externalTE"
order="23"
order="23"
/>
<ip:udp-outbound-channel-adapter id="testOutUdpiMulticast"
<ip:udp-outbound-channel-adapter id="testOutUdpiMulticast"
ack-host="somehost"
ack-port="#{tcpIpUtils.findAvailableUdpSocket(5500)}"
ack-timeout="51"
@@ -104,45 +108,50 @@
port="#{tcpIpUtils.findAvailableServerSocket(5700)}"
host="localhost"
lookup-host="false"
apply-sequence="false"
/>
<ip:tcp-outbound-channel-adapter id="testOutTcpNio"
<ip:tcp-outbound-channel-adapter id="testOutTcpNio"
channel="tcpChannel"
connection-factory="cfC1"
order="35"
auto-startup="false"
phase="125"
/>
<ip:tcp-connection-factory id="cfS2"
type="server"
port="#{tcpIpUtils.findAvailableServerSocket(5800)}"
/>
<ip:tcp-inbound-gateway id="inGateway1"
request-channel="tcpChannel"
reply-channel="replyChannel"
error-channel="errorChannel"
connection-factory="cfS2"
reply-timeout="456"
/>
auto-startup="false"
phase="126"
/>
<ip:tcp-connection-factory id="cfS3"
type="server"
port="#{tcpIpUtils.findAvailableServerSocket(5850)}"
/>
<ip:tcp-inbound-gateway id="inGateway2"
request-channel="tcpChannel"
reply-channel="replyChannel"
connection-factory="cfS3"
reply-timeout="456"
/>
/>
<ip:tcp-connection-factory id="cfC2"
type="client"
port="#{tcpIpUtils.findAvailableServerSocket(5900)}"
host="localhost"
/>
<ip:tcp-outbound-gateway id="outGateway"
request-channel="tcpChannel"
reply-channel="replyChannel"
@@ -150,7 +159,9 @@
request-timeout="234"
reply-timeout="567"
order="24"
/>
auto-startup="false"
phase="127"
/>
<ip:tcp-connection-factory
id="client1"
@@ -169,15 +180,15 @@
using-nio="#{props['use.nio']}"
single-use="true"
task-executor="externalTE"
pool-size="321"
pool-size="321"
using-direct-buffers="true"
interceptor-factory-chain="interceptors"
/>
<util:properties id="props">
<prop key="use.nio">true</prop>
</util:properties>
<ip:tcp-connection-factory
id="server1"
type="server"
@@ -199,7 +210,7 @@
using-direct-buffers="true"
interceptor-factory-chain="interceptors"
/>
<ip:tcp-connection-factory
id="client2"
type="client"
@@ -217,10 +228,10 @@
using-nio="false"
single-use="true"
task-executor="externalTE"
pool-size="321"
pool-size="321"
interceptor-factory-chain="interceptors"
/>
<ip:tcp-connection-factory
id="server2"
type="server"
@@ -241,32 +252,29 @@
pool-size="123"
interceptor-factory-chain="interceptors"
/>
<bean id="interceptors" class="org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorFactoryChain" />
<bean id="defaultSerializer" class="org.springframework.core.serializer.DefaultSerializer" />
<bean id="defaultDeserializer" class="org.springframework.core.serializer.DefaultDeserializer" />
<ip:tcp-outbound-channel-adapter id="tcpNewOut1"
channel="tcpChannel"
connection-factory="client1"
connection-factory="client1"
order="25"/>
<ip:tcp-outbound-channel-adapter id="tcpNewOut2"
channel="tcpChannel"
connection-factory="server1"
connection-factory="server1"
order="15"/>
<ip:tcp-inbound-channel-adapter id="tcpNewIn1"
channel="tcpChannel"
connection-factory="client1" />
<ip:tcp-inbound-channel-adapter id="tcpNewIn2"
channel="tcpChannel"
connection-factory="server1" />
</beans>
</beans>

View File

@@ -40,6 +40,7 @@ import org.springframework.core.task.TaskExecutor;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.ip.tcp.TcpInboundGateway;
import org.springframework.integration.ip.tcp.TcpOutboundGateway;
import org.springframework.integration.ip.tcp.TcpReceivingChannelAdapter;
@@ -68,7 +69,7 @@ public class ParserUnitTests {
@Autowired
ApplicationContext ctx;
@Autowired
@Qualifier(value="testInUdp")
UnicastReceivingChannelAdapter udpIn;
@@ -80,7 +81,7 @@ public class ParserUnitTests {
@Autowired
@Qualifier(value="testInTcp")
TcpReceivingChannelAdapter tcpIn;
@Autowired
@Qualifier(value="org.springframework.integration.ip.udp.UnicastSendingMessageHandler#0")
UnicastSendingMessageHandler udpOut;
@@ -93,6 +94,9 @@ public class ParserUnitTests {
@Qualifier(value="org.springframework.integration.ip.tcp.TcpSendingMessageHandler#0")
TcpSendingMessageHandler tcpOut;
@Autowired
EventDrivenConsumer testOutTcpNio;
@Autowired
@Qualifier(value="inGateway1")
TcpInboundGateway tcpInboundGateway1;
@@ -104,11 +108,14 @@ public class ParserUnitTests {
@Autowired
@Qualifier(value="org.springframework.integration.ip.tcp.TcpOutboundGateway#0")
TcpOutboundGateway tcpOutboundGateway;
@Autowired
EventDrivenConsumer outGateway;
@Autowired
@Qualifier(value="externalTE")
TaskExecutor taskExecutor;
@Autowired
AbstractConnectionFactory client1;
@@ -158,7 +165,7 @@ public class ParserUnitTests {
@Autowired
private MessageChannel errorChannel;
@Autowired
private DirectChannel udpChannel;
@@ -183,7 +190,7 @@ public class ParserUnitTests {
DirectFieldAccessor mapperAccessor = new DirectFieldAccessor(mapper);
assertFalse((Boolean)mapperAccessor.getPropertyValue("lookupHost"));
}
@Test
public void testInUdpMulticast() {
DirectFieldAccessor dfa = new DirectFieldAccessor(udpInMulticast);
@@ -195,13 +202,13 @@ public class ParserUnitTests {
assertEquals(31, dfa.getPropertyValue("soSendBufferSize"));
assertEquals(32, dfa.getPropertyValue("soTimeout"));
assertEquals("127.0.0.1", dfa.getPropertyValue("localAddress"));
assertNotSame(taskExecutor, dfa.getPropertyValue("taskExecutor"));
assertNotSame(taskExecutor, dfa.getPropertyValue("taskExecutor"));
assertNull(dfa.getPropertyValue("errorChannel"));
DatagramPacketMessageMapper mapper = (DatagramPacketMessageMapper) dfa.getPropertyValue("mapper");
DirectFieldAccessor mapperAccessor = new DirectFieldAccessor(mapper);
assertTrue((Boolean)mapperAccessor.getPropertyValue("lookupHost"));
}
@Test
public void testInTcp() {
DirectFieldAccessor dfa = new DirectFieldAccessor(tcpIn);
@@ -210,8 +217,12 @@ public class ParserUnitTests {
assertEquals("ip:tcp-inbound-channel-adapter", tcpIn.getComponentType());
assertEquals(errorChannel, dfa.getPropertyValue("errorChannel"));
assertFalse(cfS1.isLookupHost());
assertFalse(tcpIn.isAutoStartup());
assertEquals(124, tcpIn.getPhase());
assertTrue((Boolean) TestUtils.getPropertyValue(
TestUtils.getPropertyValue(cfS1, "mapper"), "applySequence"));
}
@Test
public void testOutUdp() {
DirectFieldAccessor dfa = new DirectFieldAccessor(udpOut);
@@ -235,7 +246,7 @@ public class ParserUnitTests {
assertEquals("testOutUdp",udpOut.getComponentName());
assertEquals("ip:udp-outbound-channel-adapter", udpOut.getComponentType());
}
@Test
public void testOutUdpMulticast() {
DirectFieldAccessor dfa = new DirectFieldAccessor(udpOutMulticast);
@@ -254,9 +265,9 @@ public class ParserUnitTests {
assertEquals(53, dfa.getPropertyValue("soSendBufferSize"));
assertEquals(54, dfa.getPropertyValue("soTimeout"));
assertEquals(55, dfa.getPropertyValue("timeToLive"));
assertEquals(12, dfa.getPropertyValue("order"));
assertEquals(12, dfa.getPropertyValue("order"));
}
@Test
public void testUdpOrder() {
@SuppressWarnings("unchecked")
@@ -277,6 +288,10 @@ public class ParserUnitTests {
assertEquals("ip:tcp-outbound-channel-adapter", tcpOut.getComponentType());
assertFalse(cfC1.isLookupHost());
assertEquals(35, dfa.getPropertyValue("order"));
assertFalse(tcpOut.isAutoStartup());
assertEquals(125, tcpOut.getPhase());
assertFalse((Boolean) TestUtils.getPropertyValue(
TestUtils.getPropertyValue(cfC1, "mapper"), "applySequence"));
}
@Test
@@ -288,6 +303,10 @@ public class ParserUnitTests {
assertEquals("ip:tcp-inbound-gateway", tcpInboundGateway1.getComponentType());
assertEquals(errorChannel, dfa.getPropertyValue("errorChannel"));
assertTrue(cfS2.isLookupHost());
assertFalse(tcpInboundGateway1.isAutoStartup());
assertEquals(126, tcpInboundGateway1.getPhase());
assertFalse((Boolean) TestUtils.getPropertyValue(
TestUtils.getPropertyValue(cfS2, "mapper"), "applySequence"));
}
@Test
@@ -309,7 +328,9 @@ public class ParserUnitTests {
assertEquals("outGateway",tcpOutboundGateway.getComponentName());
assertEquals("ip:tcp-outbound-gateway", tcpOutboundGateway.getComponentType());
assertTrue(cfC2.isLookupHost());
assertEquals(24, dfa.getPropertyValue("order"));
assertEquals(24, dfa.getPropertyValue("order"));
assertFalse(tcpOutboundGateway.isAutoStartup());
assertEquals(127, tcpOutboundGateway.getPhase());
}
@Test
@@ -350,7 +371,7 @@ public class ParserUnitTests {
assertSame(taskExecutor, dfa.getPropertyValue("taskExecutor"));
assertEquals(123, dfa.getPropertyValue("poolSize"));
assertEquals(true, dfa.getPropertyValue("usingDirectBuffers"));
assertNotNull(dfa.getPropertyValue("interceptorFactoryChain"));
assertNotNull(dfa.getPropertyValue("interceptorFactoryChain"));
}
@Test
@@ -389,7 +410,7 @@ public class ParserUnitTests {
assertEquals(true, dfa.getPropertyValue("singleUse"));
assertSame(taskExecutor, dfa.getPropertyValue("taskExecutor"));
assertEquals(123, dfa.getPropertyValue("poolSize"));
assertNotNull(dfa.getPropertyValue("interceptorFactoryChain"));
assertNotNull(dfa.getPropertyValue("interceptorFactoryChain"));
}
@Test
@@ -398,36 +419,38 @@ public class ParserUnitTests {
assertSame(client1, dfa.getPropertyValue("clientConnectionFactory"));
assertEquals(25, dfa.getPropertyValue("order"));
}
@Test
public void testNewOut2() {
DirectFieldAccessor dfa = new DirectFieldAccessor(tcpNewOut2);
assertSame(server1, dfa.getPropertyValue("serverConnectionFactory"));
assertEquals(15, dfa.getPropertyValue("order"));
assertEquals(15, dfa.getPropertyValue("order"));
}
@Test
public void testNewIn1() {
DirectFieldAccessor dfa = new DirectFieldAccessor(tcpNewIn1);
assertSame(client1, dfa.getPropertyValue("clientConnectionFactory"));
assertNull(dfa.getPropertyValue("errorChannel"));
}
@Test
public void testNewIn2() {
DirectFieldAccessor dfa = new DirectFieldAccessor(tcpNewIn2);
assertSame(server1, dfa.getPropertyValue("serverConnectionFactory"));
}
@Test
public void testtCPOrder() {
this.outGateway.start();
this.testOutTcpNio.start();
@SuppressWarnings("unchecked")
Set<MessageHandler> handlers = (Set<MessageHandler>) TestUtils
.getPropertyValue(
TestUtils.getPropertyValue(this.tcpChannel, "dispatcher"),
"handlers");
Iterator<MessageHandler> iterator = handlers.iterator();
assertSame(this.tcpNewOut2, iterator.next()); //15
assertSame(this.tcpNewOut2, iterator.next()); //15
assertSame(this.tcpOutboundGateway, iterator.next()); //24
assertSame(this.tcpNewOut1, iterator.next()); //25
assertSame(this.tcpOut, iterator.next()); //35

View File

@@ -0,0 +1,28 @@
<?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:ip="http://www.springframework.org/schema/integration/ip"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/integration/ip http://www.springframework.org/schema/integration/ip/spring-integration-ip-2.1.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="tcpIpUtils" class="org.springframework.integration.ip.util.SocketTestUtils" />
<ip:tcp-connection-factory id="cfS1"
type="server"
port="#{tcpIpUtils.findAvailableServerSocket(15200)}"
lookup-host="false"
/>
<ip:tcp-inbound-channel-adapter id="tcpNetIn"
channel="tcpChannel1"
error-channel="errorChannel"
connection-factory="cfS1"
auto-startup="false"
/>
<int:channel id="tcpChannel1">
<int:queue/>
</int:channel>
</beans>

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.ip.tcp;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @since 2.1
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class AutoStartTests {
@Autowired
AbstractServerConnectionFactory cfS1;
@Autowired
TcpReceivingChannelAdapter tcpNetIn;
@Test
public void testNetIn() throws Exception {
assertFalse(cfS1.isAutoStartup());
DirectFieldAccessor dfa = new DirectFieldAccessor(cfS1);
assertNull(dfa.getPropertyValue("serverSocket"));
startAndStop();
assertNull(dfa.getPropertyValue("serverSocket"));
startAndStop();
assertNull(dfa.getPropertyValue("serverSocket"));
}
/**
* @throws InterruptedException
*/
private void startAndStop() throws InterruptedException {
tcpNetIn.start();
int n = 0;
while (!cfS1.isListening()) {
Thread.sleep(100);
if (n++ > 100) {
fail("Failed to start listening");
}
}
tcpNetIn.stop();
while (cfS1.isListening()) {
Thread.sleep(100);
if (n++ > 100) {
fail("Failed to stop listening");
}
}
}
}

View File

@@ -86,6 +86,7 @@ public class ConnectionToConnectionTests {
throw new Exception("Failed to listen");
}
}
client.start();
for (int i = 0; i < 100; i++) {
TcpConnection connection = client.getConnection();
connection.send(MessageBuilder.withPayload("Test").build());
@@ -105,6 +106,7 @@ public class ConnectionToConnectionTests {
ByteArrayRawSerializer serializer = new ByteArrayRawSerializer();
client.setSerializer(serializer);
server.setDeserializer(serializer);
client.start();
TcpConnection connection = client.getConnection();
connection.send(MessageBuilder.withPayload("Test").build());
Message<?> message = serverSideChannel.receive(10000);
@@ -119,6 +121,7 @@ public class ConnectionToConnectionTests {
@Test
public void testLookup() throws Exception {
client.start();
TcpConnection connection = client.getConnection();
assertFalse(connection.getConnectionId().contains("localhost"));
connection.close();

View File

@@ -133,6 +133,7 @@ public class TcpConfigOutboundGatewayTests {
@Test
public void testOutboundStxEtx() throws Exception {
TcpOutboundGateway gateway = new TcpOutboundGateway();
stxEtxClient.start();
gateway.setConnectionFactory(stxEtxClient);
waitListening(inboundGatewayStxEtx);
Message<String> message = MessageBuilder.withPayload("test").build();
@@ -144,6 +145,7 @@ public class TcpConfigOutboundGatewayTests {
@Test
public void testOutboundSerialized() throws Exception {
TcpOutboundGateway gateway = new TcpOutboundGateway();
javaSerialClient.start();
gateway.setConnectionFactory(javaSerialClient);
waitListening(inboundGatewaySerialized);
Message<String> message = MessageBuilder.withPayload("test").build();
@@ -155,6 +157,7 @@ public class TcpConfigOutboundGatewayTests {
@Test
public void testOutboundLength() throws Exception {
TcpOutboundGateway gateway = new TcpOutboundGateway();
lengthHeaderClient.start();
gateway.setConnectionFactory(lengthHeaderClient);
waitListening(inboundGatewayLength);
Message<String> message = MessageBuilder.withPayload("test").build();

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.ip.tcp.connection;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -26,8 +27,6 @@ import javax.net.SocketFactory;
import org.junit.Test;
import org.springframework.integration.Message;
import org.springframework.integration.ip.IpHeaders;
import org.springframework.integration.ip.tcp.connection.TcpConnection;
import org.springframework.integration.ip.tcp.connection.TcpMessageMapper;
import org.springframework.integration.support.MessageBuilder;
/**
@@ -41,10 +40,6 @@ public class TcpMessageMapperTests {
*/
private static final String TEST_PAYLOAD = "abcdefghijkl";
/**
* Test method for {@link org.springframework.integration.ip.tcp.SocketMessageMapper#toMessage(org.springframework.integration.ip.tcp.SocketReader)}.
* Tests segmented reads into the payload and verifies reassembly.
*/
@Test
public void testToMessage() throws Exception {
@@ -64,10 +59,7 @@ public class TcpMessageMapperTests {
.getHeaders().get(IpHeaders.REMOTE_PORT));
}
/**
* Test method for {@link org.springframework.integration.ip.tcp.SocketMessageMapper#toMessage(org.springframework.integration.ip.tcp.SocketReader)}.
* Tests segmented reads into the payload and verifies reassembly.
*/
@SuppressWarnings("deprecation")
@Test
public void testToMessageSequence() throws Exception {
@@ -119,10 +111,67 @@ public class TcpMessageMapperTests {
.getHeaders().get(IpHeaders.CONNECTION_SEQ));
}
/**
* Test method for {@link org.springframework.integration.ip.tcp.SocketMessageMapper#fromMessage(org.springframework.integration.Message)}.
* @throws Exception
*/
@SuppressWarnings("deprecation")
@Test
public void testToMessageSequenceNew() throws Exception {
TcpMessageMapper mapper = new TcpMessageMapper();
mapper.setApplySequence(true);
Socket socket = SocketFactory.getDefault().createSocket();
TcpConnection connection = new AbstractTcpConnection(socket, false, false) {
public void run() {
}
public void send(Message<?> message) throws Exception {
}
public boolean isOpen() {
return false;
}
public int getPort() {
return 1234;
}
public Object getPayload() throws Exception {
return TEST_PAYLOAD.getBytes();
}
public String getHostName() {
return "MyHost";
}
public String getHostAddress() {
return "1.1.1.1";
}
public String getConnectionId() {
return "anId";
}
};
Message<Object> message = mapper.toMessage(connection);
assertEquals(TEST_PAYLOAD, new String((byte[]) message.getPayload()));
assertEquals("MyHost", message
.getHeaders().get(IpHeaders.HOSTNAME));
assertEquals("1.1.1.1", message
.getHeaders().get(IpHeaders.IP_ADDRESS));
assertEquals(1234, message
.getHeaders().get(IpHeaders.REMOTE_PORT));
assertNull(message
.getHeaders().get(IpHeaders.CONNECTION_SEQ));
assertEquals(Integer.valueOf(1), message
.getHeaders().getSequenceNumber());
assertEquals(message.getHeaders().get(IpHeaders.CONNECTION_ID), message
.getHeaders().getCorrelationId());
message = mapper.toMessage(connection);
assertEquals(TEST_PAYLOAD, new String((byte[]) message.getPayload()));
assertEquals("MyHost", message
.getHeaders().get(IpHeaders.HOSTNAME));
assertEquals("1.1.1.1", message
.getHeaders().get(IpHeaders.IP_ADDRESS));
assertEquals(1234, message
.getHeaders().get(IpHeaders.REMOTE_PORT));
assertNull(message
.getHeaders().get(IpHeaders.CONNECTION_SEQ));
assertEquals(Integer.valueOf(2), message
.getHeaders().getSequenceNumber());
assertEquals(message.getHeaders().get(IpHeaders.CONNECTION_ID), message
.getHeaders().getCorrelationId());
}
@Test
public void testFromMessageBytes() throws Exception {
String s = "test";