INT-1145 INT-1146 Add 'close' attribute to tcp gateways and inbound adapters

This commit is contained in:
Gary Russell
2010-05-28 21:55:36 +00:00
parent af2eafabbe
commit 8119caa249
31 changed files with 493 additions and 110 deletions

View File

@@ -42,6 +42,8 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
protected volatile boolean active; protected volatile boolean active;
protected volatile boolean listening;
public AbstractInternetProtocolReceivingChannelAdapter(int port) { public AbstractInternetProtocolReceivingChannelAdapter(int port) {
this.port = port; this.port = port;
@@ -95,4 +97,8 @@ public abstract class AbstractInternetProtocolReceivingChannelAdapter
this.active = false; this.active = false;
} }
public boolean isListening() {
return listening;
}
} }

View File

@@ -87,6 +87,8 @@ public abstract class IpAdapterParserUtils {
static final String SO_TRAFFIC_CLASS = "so-traffic-class"; static final String SO_TRAFFIC_CLASS = "so-traffic-class";
static final String CLOSE = "close";
/** /**
* Adds a constructor-arg to the provided bean definition builder * Adds a constructor-arg to the provided bean definition builder
@@ -172,6 +174,22 @@ public abstract class IpAdapterParserUtils {
return multicast; return multicast;
} }
/**
* Sets the close attribute, if present.
* @param element
*/
static void setClose(Element element, BeanDefinitionBuilder builder) {
String close = element.getAttribute(IpAdapterParserUtils.CLOSE);
if (!StringUtils.hasText(close)) {
close = "false";
}
if (close.equals("true")) {
builder.addPropertyValue(
Conventions.attributeNameToPropertyName(IpAdapterParserUtils.CLOSE),
close);
}
}
/** /**
* Gets the use-nio attribute, if present; if not returns 'false'. * Gets the use-nio attribute, if present; if not returns 'false'.
* @param element * @param element

View File

@@ -124,6 +124,7 @@ public class IpInboundChannelAdapterParser extends AbstractChannelAdapterParser
IpAdapterParserUtils.USING_DIRECT_BUFFERS); IpAdapterParserUtils.USING_DIRECT_BUFFERS);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
IpAdapterParserUtils.SO_KEEP_ALIVE); IpAdapterParserUtils.SO_KEEP_ALIVE);
IpAdapterParserUtils.setClose(element, builder);
return builder; return builder;
} }

View File

@@ -26,6 +26,7 @@ import org.w3c.dom.Element;
* Parser for the <outbound-gateway> element of the integration 'jms' namespace. * Parser for the <outbound-gateway> element of the integration 'jms' namespace.
* *
* @author Gary Russell * @author Gary Russell
* @since 2.0
*/ */
public class IpOutboundGatewayParser extends AbstractConsumerEndpointParser { public class IpOutboundGatewayParser extends AbstractConsumerEndpointParser {
@@ -44,6 +45,7 @@ public class IpOutboundGatewayParser extends AbstractConsumerEndpointParser {
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
IpAdapterParserUtils.CUSTOM_SOCKET_READER_CLASS_NAME); IpAdapterParserUtils.CUSTOM_SOCKET_READER_CLASS_NAME);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel"); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel");
IpAdapterParserUtils.setClose(element, builder);
return builder; return builder;
} }

View File

@@ -33,6 +33,7 @@ import java.io.IOException;
* when the format is {@link MessageFormats#FORMAT_CUSTOM}. * when the format is {@link MessageFormats#FORMAT_CUSTOM}.
* *
* @author Gary Russell * @author Gary Russell
* @since 2.0
* *
*/ */
public abstract class AbstractSocketReader implements SocketReader, MessageFormats { public abstract class AbstractSocketReader implements SocketReader, MessageFormats {
@@ -52,21 +53,21 @@ public abstract class AbstractSocketReader implements SocketReader, MessageForma
* @return True when a message is completely assembled. * @return True when a message is completely assembled.
* @throws IOException * @throws IOException
*/ */
protected abstract boolean assembleDataLengthFormat() throws IOException; protected abstract int assembleDataLengthFormat() throws IOException;
/** /**
* Assembles data in format {@link #FORMAT_STX_ETX}. * Assembles data in format {@link #FORMAT_STX_ETX}.
* @return True when a message is completely assembled. * @return True when a message is completely assembled.
* @throws IOException * @throws IOException
*/ */
protected abstract boolean assembleDataStxEtxFormat() throws IOException; protected abstract int assembleDataStxEtxFormat() throws IOException;
/** /**
* Assembles data in format {@link #FORMAT_CRLF}. * Assembles data in format {@link #FORMAT_CRLF}.
* @return True when a message is completely assembled. * @return True when a message is completely assembled.
* @throws IOException * @throws IOException
*/ */
protected abstract boolean assembleDataCrLfFormat() throws IOException; protected abstract int assembleDataCrLfFormat() throws IOException;
/** /**
* Assembles data in format {@link #FORMAT_CUSTOM}. Implementations must * Assembles data in format {@link #FORMAT_CUSTOM}. Implementations must
@@ -76,9 +77,9 @@ public abstract class AbstractSocketReader implements SocketReader, MessageForma
* @return True when a message is completely assembled. * @return True when a message is completely assembled.
* @throws IOException * @throws IOException
*/ */
protected abstract boolean assembleDataCustomFormat() throws IOException; protected abstract int assembleDataCustomFormat() throws IOException;
public boolean assembleData() throws IOException { public int assembleData() throws IOException {
try { try {
switch (this.messageFormat) { switch (this.messageFormat) {
case FORMAT_LENGTH_HEADER: case FORMAT_LENGTH_HEADER:

View File

@@ -17,6 +17,9 @@ package org.springframework.integration.ip.tcp;
import java.io.IOException; import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/** /**
* Abstract SocketWriter that handles data in 3 standard, and one custom * Abstract SocketWriter that handles data in 3 standard, and one custom
* format. The default format is {@link MessageFormats#FORMAT_LENGTH_HEADER} in which * format. The default format is {@link MessageFormats#FORMAT_LENGTH_HEADER} in which
@@ -31,14 +34,18 @@ import java.io.IOException;
* appropriate implementation, and provide an implementation for * appropriate implementation, and provide an implementation for
* {@link #writeCustomFormat(byte[])} which is invoked by {@link #write(byte[])} * {@link #writeCustomFormat(byte[])} which is invoked by {@link #write(byte[])}
* when the format is {@link MessageFormats#FORMAT_CUSTOM}. * when the format is {@link MessageFormats#FORMAT_CUSTOM}.
*
* @author Gary Russell * @author Gary Russell
* @since 2.0
* *
*/ */
public abstract class AbstractSocketWriter implements SocketWriter, MessageFormats { public abstract class AbstractSocketWriter implements SocketWriter, MessageFormats {
protected int messageFormat = FORMAT_LENGTH_HEADER; protected int messageFormat = FORMAT_LENGTH_HEADER;
/* protected final Log logger = LogFactory.getLog(this.getClass());
/*
* @see org.springframework.integration.ip.tcp.SocketWriter#write(byte[]) * @see org.springframework.integration.ip.tcp.SocketWriter#write(byte[])
*/ */
public synchronized void write(byte[] bytes) throws IOException { public synchronized void write(byte[] bytes) throws IOException {

View File

@@ -29,6 +29,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
* are provided. * are provided.
* *
* @author Gary Russell * @author Gary Russell
* @since 2.0
* *
*/ */
public abstract class AbstractTcpReceivingChannelAdapter extends public abstract class AbstractTcpReceivingChannelAdapter extends
@@ -42,7 +43,9 @@ public abstract class AbstractTcpReceivingChannelAdapter extends
protected volatile boolean soKeepAlive; protected volatile boolean soKeepAlive;
protected int messageFormat = MessageFormats.FORMAT_LENGTH_HEADER; protected volatile int messageFormat = MessageFormats.FORMAT_LENGTH_HEADER;
protected volatile boolean close;
/** /**
* Constructs a receiving channel adapter that listens on the port. * Constructs a receiving channel adapter that listens on the port.
@@ -122,4 +125,11 @@ public abstract class AbstractTcpReceivingChannelAdapter extends
this.poolSize = poolSize; this.poolSize = poolSize;
} }
/**
* @param close the close to set
*/
public void setClose(boolean close) {
this.close = close;
}
} }

View File

@@ -32,6 +32,7 @@ import org.springframework.integration.message.MessageMappingException;
* is completely assembled. * is completely assembled.
* *
* @author Gary Russell * @author Gary Russell
* @since 2.0
* *
*/ */
public class NetSocketReader extends AbstractSocketReader { public class NetSocketReader extends AbstractSocketReader {
@@ -52,9 +53,11 @@ public class NetSocketReader extends AbstractSocketReader {
* @see org.springframework.integration.ip.tcp.SocketReader#read(java.nio.ByteBuffer) * @see org.springframework.integration.ip.tcp.SocketReader#read(java.nio.ByteBuffer)
*/ */
@Override @Override
protected boolean assembleDataLengthFormat() throws IOException { protected int assembleDataLengthFormat() throws IOException {
byte[] lengthPart = new byte[4]; byte[] lengthPart = new byte[4];
read(lengthPart); int status = read(lengthPart, true);
if (status < 0)
return status;
int messageLength = ByteBuffer.wrap(lengthPart).getInt(); int messageLength = ByteBuffer.wrap(lengthPart).getInt();
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Message length is " + messageLength); logger.debug("Message length is " + messageLength);
@@ -64,27 +67,27 @@ public class NetSocketReader extends AbstractSocketReader {
" exceeds max message length: " + this.maxMessageSize); " exceeds max message length: " + this.maxMessageSize);
} }
byte[] messagePart = new byte[messageLength]; byte[] messagePart = new byte[messageLength];
read(messagePart); read(messagePart, false);
assembledData = messagePart; assembledData = messagePart;
return true; return MESSAGE_COMPLETE;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.springframework.integration.ip.tcp.AbstractSocketReader#assembleDataStxEtxFormat() * @see org.springframework.integration.ip.tcp.AbstractSocketReader#assembleDataStxEtxFormat()
*/ */
@Override @Override
protected boolean assembleDataStxEtxFormat() throws IOException { protected int assembleDataStxEtxFormat() throws IOException {
InputStream inputStream = socket.getInputStream(); InputStream inputStream = socket.getInputStream();
if (inputStream.read() != STX) int bite = inputStream.read();
if (bite < 0) {
return bite;
}
if (bite != STX)
throw new MessageMappingException("Expected STX to begin message"); throw new MessageMappingException("Expected STX to begin message");
byte[] buffer = new byte[this.maxMessageSize]; byte[] buffer = new byte[this.maxMessageSize];
int n = 0; int n = 0;
int bite;
while ((bite = inputStream.read()) != ETX) { while ((bite = inputStream.read()) != ETX) {
if (bite < 0) { checkClosure(bite);
logger.debug("Socket closed");
throw new IOException("Socket Closed");
}
buffer[n++] = (byte) bite; buffer[n++] = (byte) bite;
if (n >= this.maxMessageSize) { if (n >= this.maxMessageSize) {
throw new IOException("ETX not found before max message length: " throw new IOException("ETX not found before max message length: "
@@ -93,24 +96,31 @@ public class NetSocketReader extends AbstractSocketReader {
} }
assembledData = new byte[n]; assembledData = new byte[n];
System.arraycopy(buffer, 0, assembledData, 0, n); System.arraycopy(buffer, 0, assembledData, 0, n);
return true; return MESSAGE_COMPLETE;
}
private void checkClosure(int bite) throws IOException {
if (bite < 0) {
logger.debug("Socket closed");
throw new IOException("Socket closed");
}
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.springframework.integration.ip.tcp.AbstractSocketReader#assembleDataCrLfFormat() * @see org.springframework.integration.ip.tcp.AbstractSocketReader#assembleDataCrLfFormat()
*/ */
@Override @Override
protected boolean assembleDataCrLfFormat() throws IOException { protected int assembleDataCrLfFormat() throws IOException {
InputStream inputStream = socket.getInputStream(); InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[this.maxMessageSize]; byte[] buffer = new byte[this.maxMessageSize];
int n = 0; int n = 0;
int bite; int bite;
while (true) { while (true) {
bite = inputStream.read(); bite = inputStream.read();
if (bite < 0) { if (bite < 0 && n == 0) {
logger.debug("Socket closed"); return bite;
throw new IOException("Socket Closed");
} }
checkClosure(bite);
if (n > 0 && bite == '\n' && buffer[n-1] == '\r') if (n > 0 && bite == '\n' && buffer[n-1] == '\r')
break; break;
buffer[n++] = (byte) bite; buffer[n++] = (byte) bite;
@@ -121,7 +131,7 @@ public class NetSocketReader extends AbstractSocketReader {
}; };
assembledData = new byte[n-1]; assembledData = new byte[n-1];
System.arraycopy(buffer, 0, assembledData, 0, n-1); System.arraycopy(buffer, 0, assembledData, 0, n-1);
return true; return MESSAGE_COMPLETE;
} }
/** /**
@@ -132,7 +142,7 @@ public class NetSocketReader extends AbstractSocketReader {
* *
*/ */
@Override @Override
protected boolean assembleDataCustomFormat() throws IOException { protected int assembleDataCustomFormat() throws IOException {
throw new UnsupportedOperationException("Need to subclass for this format"); throw new UnsupportedOperationException("Need to subclass for this format");
} }
@@ -149,19 +159,23 @@ public class NetSocketReader extends AbstractSocketReader {
* Reads data from the socket and puts the data in buffer. Blocks until * Reads data from the socket and puts the data in buffer. Blocks until
* buffer is full or a socket timeout occurs. * buffer is full or a socket timeout occurs.
* @param buffer * @param buffer
* @param header true if we are reading the header
* @return < 0 if socket closed and not in the middle of a message
* @throws IOException * @throws IOException
*/ */
protected void read(byte[] buffer) throws IOException { protected int read(byte[] buffer, boolean header) throws IOException {
int lengthRead = 0; int lengthRead = 0;
int needed = buffer.length; int needed = buffer.length;
while (lengthRead < needed) { while (lengthRead < needed) {
int len; int len;
len = socket.getInputStream().read(buffer, lengthRead, len = socket.getInputStream().read(buffer, lengthRead,
needed - lengthRead); needed - lengthRead);
if (len < 0) { if (len < 0 && header && lengthRead == 0) {
logger.debug("Socket closed"); return len;
throw new IOException("Socket Closed");
} }
if (len < 0)
logger.debug("socket closed after " + lengthRead + " of " + needed);
checkClosure(len);
lengthRead += len; lengthRead += len;
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Read " + len + " bytes, buffer is now at " + logger.debug("Read " + len + " bytes, buffer is now at " +
@@ -169,7 +183,7 @@ public class NetSocketReader extends AbstractSocketReader {
needed); needed);
} }
} }
return 0;
} }
/* (non-Javadoc) /* (non-Javadoc)

View File

@@ -25,6 +25,7 @@ import java.nio.ByteBuffer;
* data is wrapped in a wire protocol based on the messageFormat property. * data is wrapped in a wire protocol based on the messageFormat property.
* *
* @author Gary Russell * @author Gary Russell
* @since 2.0
*/ */
public class NetSocketWriter extends AbstractSocketWriter { public class NetSocketWriter extends AbstractSocketWriter {
@@ -87,6 +88,8 @@ public class NetSocketWriter extends AbstractSocketWriter {
protected void doClose() { protected void doClose() {
try { try {
socket.close(); socket.close();
} catch (IOException e) {} } catch (IOException e) {
logger.error("Error on close", e);
}
} }
} }

View File

@@ -30,6 +30,7 @@ import org.springframework.integration.message.MessageMappingException;
* A non-blocking SocketReader that reads from a {@link java.nio.channels.SocketChannel}. * A non-blocking SocketReader that reads from a {@link java.nio.channels.SocketChannel}.
* *
* @author Gary Russell * @author Gary Russell
* @since 2.0
* *
*/ */
public class NioSocketReader extends AbstractSocketReader { public class NioSocketReader extends AbstractSocketReader {
@@ -71,13 +72,13 @@ public class NioSocketReader extends AbstractSocketReader {
* @see org.springframework.integration.ip.tcp.SocketReader#assembleData() * @see org.springframework.integration.ip.tcp.SocketReader#assembleData()
*/ */
@Override @Override
public boolean assembleDataLengthFormat() throws IOException { public int assembleDataLengthFormat() throws IOException {
if (lengthPart == null) { if (lengthPart == null) {
lengthPart = allocate(4); lengthPart = allocate(4);
} }
if (lengthPart.hasRemaining()) { if (lengthPart.hasRemaining()) {
readChannel(lengthPart); readChannel(lengthPart);
return false; return MESSAGE_INCOMPLETE;
} }
if (dataPart == null) { if (dataPart == null) {
lengthPart.flip(); lengthPart.flip();
@@ -94,19 +95,19 @@ public class NioSocketReader extends AbstractSocketReader {
if (dataPart.hasRemaining()) { if (dataPart.hasRemaining()) {
readChannel(dataPart); readChannel(dataPart);
if (dataPart.hasRemaining()) { if (dataPart.hasRemaining()) {
return false; return MESSAGE_INCOMPLETE;
} }
} }
assembledData = dataPart.array(); assembledData = dataPart.array();
lengthPart = dataPart = null; lengthPart = dataPart = null;
return true; return MESSAGE_COMPLETE;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.springframework.integration.ip.tcp.AbstractSocketReader#assembleDataStxEtxFormat() * @see org.springframework.integration.ip.tcp.AbstractSocketReader#assembleDataStxEtxFormat()
*/ */
@Override @Override
protected boolean assembleDataStxEtxFormat() throws IOException { protected int assembleDataStxEtxFormat() throws IOException {
if (readChannelNonDeterministic()) { if (readChannelNonDeterministic()) {
byte bite = rawBuffer.get(); byte bite = rawBuffer.get();
int count = 0; int count = 0;
@@ -120,12 +121,12 @@ public class NioSocketReader extends AbstractSocketReader {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Incomplete message, consumed 1 byte"); logger.debug("Incomplete message, consumed 1 byte");
} }
return false; return MESSAGE_INCOMPLETE;
} }
} else { } else {
if (bite == ETX) { if (bite == ETX) {
finishAssembly(); finishAssembly();
return true; return MESSAGE_COMPLETE;
} }
buildBuffer.put(bite); buildBuffer.put(bite);
count++; count++;
@@ -139,7 +140,7 @@ public class NioSocketReader extends AbstractSocketReader {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Incomplete message, consumed " + count + " bytes"); logger.debug("Incomplete message, consumed " + count + " bytes");
} }
return false; return MESSAGE_INCOMPLETE;
} }
bite = rawBuffer.get(); bite = rawBuffer.get();
if (bite == ETX) { if (bite == ETX) {
@@ -156,13 +157,13 @@ public class NioSocketReader extends AbstractSocketReader {
logger.debug("Consumed " + count + " bytes"); logger.debug("Consumed " + count + " bytes");
} }
finishAssembly(); finishAssembly();
return true; return MESSAGE_COMPLETE;
} else { } else {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Incomplete message, consumed 0 bytes"); logger.debug("Incomplete message, consumed 0 bytes");
} }
} }
return false; return MESSAGE_INCOMPLETE;
} }
/** /**
@@ -180,7 +181,7 @@ public class NioSocketReader extends AbstractSocketReader {
* @see org.springframework.integration.ip.tcp.AbstractSocketReader#assembleDataCrLfFormat() * @see org.springframework.integration.ip.tcp.AbstractSocketReader#assembleDataCrLfFormat()
*/ */
@Override @Override
protected boolean assembleDataCrLfFormat() throws IOException { protected int assembleDataCrLfFormat() throws IOException {
if (readChannelNonDeterministic()) { if (readChannelNonDeterministic()) {
int count = 0; int count = 0;
while (true) { while (true) {
@@ -188,7 +189,7 @@ public class NioSocketReader extends AbstractSocketReader {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Incomplete message, consumed " + count + " bytes"); logger.debug("Incomplete message, consumed " + count + " bytes");
} }
return false; return MESSAGE_INCOMPLETE;
} }
byte bite = rawBuffer.get(); byte bite = rawBuffer.get();
if (bite == '\n' && buildBuffer.position() > 0) { if (bite == '\n' && buildBuffer.position() > 0) {
@@ -209,13 +210,13 @@ public class NioSocketReader extends AbstractSocketReader {
logger.debug("Consumed " + count + " bytes"); logger.debug("Consumed " + count + " bytes");
} }
finishAssembly(); finishAssembly();
return true; return MESSAGE_COMPLETE;
} else { } else {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Incomplete message, consumed 0 bytes"); logger.debug("Incomplete message, consumed 0 bytes");
} }
} }
return false; return MESSAGE_INCOMPLETE;
} }
/** /**
@@ -226,7 +227,7 @@ public class NioSocketReader extends AbstractSocketReader {
* *
*/ */
@Override @Override
protected boolean assembleDataCustomFormat() throws IOException { protected int assembleDataCustomFormat() throws IOException {
throw new UnsupportedOperationException("Need to subclass for this format"); throw new UnsupportedOperationException("Need to subclass for this format");
} }

View File

@@ -27,6 +27,7 @@ import java.util.concurrent.LinkedBlockingQueue;
* data is wrapped in a wire protocol based on the messageFormat property. * data is wrapped in a wire protocol based on the messageFormat property.
* *
* @author Gary Russell * @author Gary Russell
* @since 2.0
*/ */
public class NioSocketWriter extends AbstractSocketWriter { public class NioSocketWriter extends AbstractSocketWriter {
@@ -250,7 +251,9 @@ public class NioSocketWriter extends AbstractSocketWriter {
protected void doClose() { protected void doClose() {
try { try {
channel.close(); channel.close();
} catch (IOException e) {} } catch (IOException e) {
logger.error("Error on close", e);
}
} }
} }

View File

@@ -15,6 +15,7 @@
*/ */
package org.springframework.integration.ip.tcp; package org.springframework.integration.ip.tcp;
import java.io.IOException;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
@@ -34,6 +35,7 @@ import org.springframework.integration.message.MessageMappingException;
* number of concurrent connections expected. * number of concurrent connections expected.
* *
* @author Gary Russell * @author Gary Russell
* @since 2.0
* *
*/ */
public class SimpleTcpNetInboundGateway extends AbstractMessagingGateway { public class SimpleTcpNetInboundGateway extends AbstractMessagingGateway {
@@ -62,6 +64,8 @@ public class SimpleTcpNetInboundGateway extends AbstractMessagingGateway {
protected Class<NetSocketWriter> customSocketWriterClass; protected Class<NetSocketWriter> customSocketWriterClass;
protected boolean close;
@Override @Override
protected void doStart() { protected void doStart() {
super.doStart(); super.doStart();
@@ -86,6 +90,7 @@ public class SimpleTcpNetInboundGateway extends AbstractMessagingGateway {
this.delegate.setSoTimeout(soTimeout); this.delegate.setSoTimeout(soTimeout);
this.delegate.setTaskScheduler(getTaskScheduler()); this.delegate.setTaskScheduler(getTaskScheduler());
this.delegate.setCustomSocketReaderClassName(customSocketReaderClassName); this.delegate.setCustomSocketReaderClassName(customSocketReaderClassName);
this.delegate.setClose(close);
super.onInit(); super.onInit();
} }
@@ -189,6 +194,17 @@ public class SimpleTcpNetInboundGateway extends AbstractMessagingGateway {
} }
} }
/**
* @param close the close to set
*/
public void setClose(boolean close) {
this.close = close;
}
public boolean isListening() {
return delegate.isListening();
}
private class WriteCapableTcpNetReceivingChannelAdapter extends TcpNetReceivingChannelAdapter { private class WriteCapableTcpNetReceivingChannelAdapter extends TcpNetReceivingChannelAdapter {
/** /**
@@ -206,6 +222,13 @@ public class SimpleTcpNetInboundGateway extends AbstractMessagingGateway {
customSocketWriterClass, socket); customSocketWriterClass, socket);
try { try {
writer.write(this.mapper.fromMessage(message)); writer.write(this.mapper.fromMessage(message));
if (close) {
try {
socket.close();
} catch (IOException ioe) {
logger.error("Error on close", ioe);
}
}
} catch (Exception e) { } catch (Exception e) {
throw new MessageMappingException("Failed to map and send response", e); throw new MessageMappingException("Failed to map and send response", e);
} }

View File

@@ -50,6 +50,8 @@ public class SimpleTcpNetOutboundGateway extends
protected NetSocketReader reader; protected NetSocketReader reader;
protected boolean close;
/** /**
* Constructs a SimpleTcpNetOutboundGateway that sends data to the * Constructs a SimpleTcpNetOutboundGateway that sends data to the
* specified host and port, and waits for a response. * specified host and port, and waits for a response.
@@ -67,9 +69,9 @@ public class SimpleTcpNetOutboundGateway extends
@Override @Override
protected synchronized Object handleRequestMessage(Message<?> requestMessage) { protected synchronized Object handleRequestMessage(Message<?> requestMessage) {
this.handler.handleMessage(requestMessage); this.handler.handleMessage(requestMessage);
Socket socket = this.handler.getSocket();
if (this.reader == null || if (this.reader == null ||
this.reader.getSocket() != handler.getSocket()) { this.reader.getSocket() != socket) { // might have re-opened on error
Socket socket = this.handler.getSocket();
this.reader = SocketIoUtils.createNetReader(this.messageFormat, this.reader = SocketIoUtils.createNetReader(this.messageFormat,
this.customSocketReaderClass, socket, this.maxMessageSize, this.customSocketReaderClass, socket, this.maxMessageSize,
this.soReceiveBufferSize); this.soReceiveBufferSize);
@@ -77,6 +79,10 @@ public class SimpleTcpNetOutboundGateway extends
try { try {
this.reader.assembleData(); // Net... always returns true this.reader.assembleData(); // Net... always returns true
byte[] bytes = this.reader.getAssembledData(); byte[] bytes = this.reader.getAssembledData();
if (close) {
logger.debug("Closing socket because close=true");
this.handler.close();
}
return bytes; return bytes;
} catch (Exception e) { } catch (Exception e) {
this.reader = null; this.reader = null;
@@ -207,4 +213,11 @@ public class SimpleTcpNetOutboundGateway extends
this.setOutputChannel(replyChannel); this.setOutputChannel(replyChannel);
} }
/**
* @param close the close to set
*/
public void setClose(boolean close) {
this.close = close;
}
} }

View File

@@ -23,19 +23,26 @@ import java.net.Socket;
* General interface for assembling message data from a TCP/IP Socket. * General interface for assembling message data from a TCP/IP Socket.
* Implementations for {@link java.net.Socket} and {@link java.nio.channels.SocketChannel} * Implementations for {@link java.net.Socket} and {@link java.nio.channels.SocketChannel}
* are provided. * are provided.
*
* @author Gary Russell * @author Gary Russell
* @since 2.0
* *
*/ */
public interface SocketReader { public interface SocketReader {
public static int MESSAGE_INCOMPLETE = 0;
public static int MESSAGE_COMPLETE = 1;
/** /**
* Reads the data the socket and assembles * Reads the data the socket and assembles
* packets of data into a complete message, depending on the format of that * packets of data into a complete message, depending on the format of that
* data. * data.
* @return true when the message is assembled. * @return MESSAGE_COMPLETE when message is assembled, otherwise MESSAGE_IMCOMPLETE, or
* < 0 if socket closed before any data for a message is received.
* @throws IOException * @throws IOException
*/ */
public boolean assembleData() throws IOException; public int assembleData() throws IOException;
/** /**
* Retrieves the assembled tcp data or null if the data is not * Retrieves the assembled tcp data or null if the data is not

View File

@@ -30,6 +30,7 @@ import org.springframework.integration.ip.util.SocketIoUtils;
* accordingly. * accordingly.
* *
* @author Gary Russell * @author Gary Russell
* @since 2.0
* *
*/ */
public class TcpNetReceivingChannelAdapter extends public class TcpNetReceivingChannelAdapter extends
@@ -58,6 +59,7 @@ public class TcpNetReceivingChannelAdapter extends
try { try {
serverSocket = ServerSocketFactory.getDefault() serverSocket = ServerSocketFactory.getDefault()
.createServerSocket(port, Math.abs(poolSize)); .createServerSocket(port, Math.abs(poolSize));
listening = true;
while (true) { while (true) {
final Socket socket = serverSocket.accept(); final Socket socket = serverSocket.accept();
setSocketOptions(socket); setSocketOptions(socket);
@@ -72,6 +74,7 @@ public class TcpNetReceivingChannelAdapter extends
serverSocket.close(); serverSocket.close();
} catch (IOException e1) {} } catch (IOException e1) {}
} }
listening = false;
serverSocket = null; serverSocket = null;
if (active) { if (active) {
logger.error("Error on ServerSocket", e); logger.error("Error on ServerSocket", e);
@@ -94,8 +97,21 @@ public class TcpNetReceivingChannelAdapter extends
this.soReceiveBufferSize); this.soReceiveBufferSize);
while (true) { while (true) {
try { try {
if (reader.assembleData()) { int messageStatus = reader.assembleData();
if (messageStatus < 0) {
return;
}
if (messageStatus == SocketReader.MESSAGE_COMPLETE) {
processMessage(reader); processMessage(reader);
if (close) {
logger.debug("Closing socket because close=true");
try {
reader.getSocket().close();
} catch (IOException ioe) {
logger.error("Error on close", ioe);
}
break;
}
} }
} catch (Exception e) { } catch (Exception e) {
logger.error("processMessage failed", e); logger.error("processMessage failed", e);

View File

@@ -25,6 +25,7 @@ import org.springframework.integration.ip.util.SocketIoUtils;
/** /**
* TCP Sending Channel Adapter that that uses a {@link java.net.Socket}. * TCP Sending Channel Adapter that that uses a {@link java.net.Socket}.
* @author Gary Russell * @author Gary Russell
* @since 2.0
* *
*/ */
public class TcpNetSendingMessageHandler extends public class TcpNetSendingMessageHandler extends
@@ -57,6 +58,7 @@ public class TcpNetSendingMessageHandler extends
protected synchronized SocketWriter getWriter() { protected synchronized SocketWriter getWriter() {
if (this.writer == null) { if (this.writer == null) {
try { try {
logger.debug("Opening new socket connection");
this.socket = SocketFactory.getDefault().createSocket(this.host, this.port); this.socket = SocketFactory.getDefault().createSocket(this.host, this.port);
this.setSocketAttributes(socket); this.setSocketAttributes(socket);
NetSocketWriter writer = SocketIoUtils.createNetWriter(messageFormat, NetSocketWriter writer = SocketIoUtils.createNetWriter(messageFormat,
@@ -85,4 +87,12 @@ public class TcpNetSendingMessageHandler extends
} }
} }
/**
* Close the underlying socket and prepare to establish a new socket on
* the next write.
*/
protected void close() {
this.writer.doClose();
this.writer = null;
}
} }

View File

@@ -38,6 +38,7 @@ import org.springframework.integration.ip.util.SocketIoUtils;
* number of threads is controlled by the poolSize property. * number of threads is controlled by the poolSize property.
* *
* @author Gary Russell * @author Gary Russell
* @since 2.0
* *
*/ */
public class TcpNioReceivingChannelAdapter extends public class TcpNioReceivingChannelAdapter extends
@@ -65,6 +66,7 @@ public class TcpNioReceivingChannelAdapter extends
protected void server() { protected void server() {
try { try {
serverChannel = ServerSocketChannel.open(); serverChannel = ServerSocketChannel.open();
listening = true;
serverChannel.configureBlocking(false); serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port), serverChannel.socket().bind(new InetSocketAddress(port),
Math.abs(poolSize)); Math.abs(poolSize));
@@ -76,6 +78,7 @@ public class TcpNioReceivingChannelAdapter extends
try { try {
serverChannel.close(); serverChannel.close();
} catch (IOException e1) { } } catch (IOException e1) { }
listening = false;
serverChannel = null; serverChannel = null;
if (active) { if (active) {
logger.error("Error on ServerSocketChannel", e); logger.error("Error on ServerSocketChannel", e);
@@ -169,11 +172,23 @@ public class TcpNioReceivingChannelAdapter extends
private void doRead(SelectionKey key) { private void doRead(SelectionKey key) {
NioSocketReader reader = (NioSocketReader) key.attachment(); NioSocketReader reader = (NioSocketReader) key.attachment();
try { try {
if (reader.assembleData()) { int messageStatus = reader.assembleData();
if (messageStatus < 0) {
return;
}
if (messageStatus == SocketReader.MESSAGE_COMPLETE) {
Message<byte[]> message; Message<byte[]> message;
message = mapper.toMessage(reader); message = mapper.toMessage(reader);
if (message != null) { if (message != null) {
sendMessage(message); sendMessage(message);
if (close) {
logger.debug("Closing channel because close=true");
try {
key.channel().close();
} catch (IOException ioe) {
logger.error("Error on close", ioe);
}
}
} }
} }
} catch (Exception e) {} } catch (Exception e) {}

View File

@@ -21,7 +21,10 @@ import org.springframework.integration.ip.util.SocketIoUtils;
/** /**
* TCP Sending Channel Adapter that that uses a {@link java.nio.channels.SocketChannel}.
*
* @author Gary Russell * @author Gary Russell
* @since 2.0
* *
*/ */
public class TcpNioSendingMessageHandler extends public class TcpNioSendingMessageHandler extends
@@ -49,6 +52,7 @@ public class TcpNioSendingMessageHandler extends
protected synchronized SocketWriter getWriter() { protected synchronized SocketWriter getWriter() {
if (this.socketChannel == null) { if (this.socketChannel == null) {
try { try {
logger.debug("Creating new SocketChannel");
this.socketChannel = SocketChannel.open(this.destinationAddress); this.socketChannel = SocketChannel.open(this.destinationAddress);
this.setSocketAttributes(socketChannel.socket()); this.setSocketAttributes(socketChannel.socket());
NioSocketWriter writer = SocketIoUtils.createNioWriter(messageFormat, NioSocketWriter writer = SocketIoUtils.createNioWriter(messageFormat,

View File

@@ -102,6 +102,8 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
this.threadPoolTaskScheduler.initialize(); this.threadPoolTaskScheduler.initialize();
} }
listening = true;
// Do as little as possible here so we can loop around and catch the next packet. // Do as little as possible here so we can loop around and catch the next packet.
// Just schedule the packet for processing. // Just schedule the packet for processing.
while (this.active) { while (this.active) {
@@ -121,6 +123,7 @@ public class UnicastReceivingChannelAdapter extends AbstractInternetProtocolRece
throw new MessagingException("failed to receive DatagramPacket", e); throw new MessagingException("failed to receive DatagramPacket", e);
} }
} }
listening = false;
} }
protected void sendAck(Message<byte[]> message) { protected void sendAck(Message<byte[]> message) {

View File

@@ -39,6 +39,15 @@ the custom message format. See java docs for TcpNetReceivingChannelAdapter and T
</xsd:documentation> </xsd:documentation>
</xsd:annotation> </xsd:annotation>
</xsd:attribute> </xsd:attribute>
<xsd:attribute name="close" type="xsd:string" default="false">
<xsd:annotation>
<xsd:documentation>
If true, the socket will be closed after a message is received and sent to the
outbound channel. If false, the socket will remain open ready to receive the
next message.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension> </xsd:extension>
</xsd:complexContent> </xsd:complexContent>
</xsd:complexType> </xsd:complexType>
@@ -202,6 +211,18 @@ the custom message format. See java docs for TcpNetSendingChannelAdapter and Tcp
<xsd:attribute name="reply-timeout" type="xsd:string"/> <xsd:attribute name="reply-timeout" type="xsd:string"/>
<xsd:attribute name="auto-startup" type="xsd:string" default="true"/> <xsd:attribute name="auto-startup" type="xsd:string" default="true"/>
<xsd:attribute name="receive-buffer-size" type="xsd:string" /> <xsd:attribute name="receive-buffer-size" type="xsd:string" />
<xsd:attribute name="close" type="xsd:string" default="false">
<xsd:annotation>
<xsd:documentation>
If true, for an outbound gateway, the socket will be closed after a response
is received and sent to the
reply channel. If false, the socket will remain open and be used to send the
next message. If true, for an inbound gateway, the socket will be closed after
the response is sent. If false, the socket will remain open and be used to
receive the next message.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension> </xsd:extension>
</xsd:complexContent> </xsd:complexContent>
</xsd:complexType> </xsd:complexType>

View File

@@ -74,6 +74,7 @@
so-timeout="32" so-timeout="32"
using-direct-buffers="true" using-direct-buffers="true"
using-nio="true" using-nio="true"
close="true"
/> />
<ip:inbound-channel-adapter id="testInTcpNet" <ip:inbound-channel-adapter id="testInTcpNet"
@@ -197,13 +198,45 @@
custom-socket-writer-class-name="org.springframework.integration.ip.tcp.CustomNetSocketWriter" custom-socket-writer-class-name="org.springframework.integration.ip.tcp.CustomNetSocketWriter"
message-format="crlf" message-format="crlf"
host="localhost" host="localhost"
port="#{tcpIpUtils.findAvailableServerSocket(6500)}" port="#{tcpIpUtils.findAvailableServerSocket(6600)}"
receive-buffer-size="223" receive-buffer-size="223"
so-keep-alive="true" so-keep-alive="true"
so-receive-buffer-size="224" so-receive-buffer-size="224"
so-send-buffer-size="225" so-send-buffer-size="225"
so-timeout="226" so-timeout="226"
close="false"
/>
<ip:inbound-gateway id="simpleInGatewayClose"
request-channel="tcpChannel"
reply-channel="replyChannel"
custom-socket-reader-class-name="org.springframework.integration.ip.tcp.CustomNetSocketReader"
custom-socket-writer-class-name="org.springframework.integration.ip.tcp.CustomNetSocketWriter"
message-format="crlf"
pool-size="23"
port="#{tcpIpUtils.findAvailableServerSocket(6700)}"
receive-buffer-size="123"
so-keep-alive="true"
so-receive-buffer-size="124"
so-send-buffer-size="125"
so-timeout="126"
close="true"
/>
<ip:outbound-gateway id="simpleOutGatewayClose"
request-channel="tcpChannel"
reply-channel="replyChannel"
custom-socket-reader-class-name="org.springframework.integration.ip.tcp.CustomNetSocketReader"
custom-socket-writer-class-name="org.springframework.integration.ip.tcp.CustomNetSocketWriter"
message-format="crlf"
host="localhost"
port="#{tcpIpUtils.findAvailableServerSocket(6800)}"
receive-buffer-size="223"
so-keep-alive="true"
so-receive-buffer-size="224"
so-send-buffer-size="225"
so-timeout="226"
close="true"
/> />
</beans> </beans>

View File

@@ -95,12 +95,21 @@ public class ParserUnitTests {
TcpNetSendingMessageHandler tcpOutNet; TcpNetSendingMessageHandler tcpOutNet;
@Autowired @Autowired
@Qualifier(value="simpleInGateway")
SimpleTcpNetInboundGateway simpleTcpNetInboundGateway; SimpleTcpNetInboundGateway simpleTcpNetInboundGateway;
@Autowired @Autowired
@Qualifier(value="org.springframework.integration.ip.tcp.SimpleTcpNetOutboundGateway#0") @Qualifier(value="org.springframework.integration.ip.tcp.SimpleTcpNetOutboundGateway#0")
SimpleTcpNetOutboundGateway simpleTcpNetOutboundGateway; SimpleTcpNetOutboundGateway simpleTcpNetOutboundGateway;
@Autowired
@Qualifier(value="simpleInGatewayClose")
SimpleTcpNetInboundGateway simpleTcpNetInboundGatewayClose;
@Autowired
@Qualifier(value="org.springframework.integration.ip.tcp.SimpleTcpNetOutboundGateway#1")
SimpleTcpNetOutboundGateway simpleTcpNetOutboundGatewayClose;
@Test @Test
public void testInUdp() { public void testInUdp() {
DirectFieldAccessor dfa = new DirectFieldAccessor(udpIn); DirectFieldAccessor dfa = new DirectFieldAccessor(udpIn);
@@ -136,6 +145,7 @@ public class ParserUnitTests {
assertEquals(29, dfa.getPropertyValue("receiveBufferSize")); assertEquals(29, dfa.getPropertyValue("receiveBufferSize"));
assertEquals(30, dfa.getPropertyValue("soReceiveBufferSize")); assertEquals(30, dfa.getPropertyValue("soReceiveBufferSize"));
assertEquals(32, dfa.getPropertyValue("soTimeout")); assertEquals(32, dfa.getPropertyValue("soTimeout"));
assertEquals(false, dfa.getPropertyValue("close"));
} }
@Test @Test
@@ -150,6 +160,7 @@ public class ParserUnitTests {
assertEquals(29, dfa.getPropertyValue("receiveBufferSize")); assertEquals(29, dfa.getPropertyValue("receiveBufferSize"));
assertEquals(30, dfa.getPropertyValue("soReceiveBufferSize")); assertEquals(30, dfa.getPropertyValue("soReceiveBufferSize"));
assertEquals(32, dfa.getPropertyValue("soTimeout")); assertEquals(32, dfa.getPropertyValue("soTimeout"));
assertEquals(true, dfa.getPropertyValue("close"));
} }
@Test @Test
@@ -163,6 +174,7 @@ public class ParserUnitTests {
assertEquals(29, dfa.getPropertyValue("receiveBufferSize")); assertEquals(29, dfa.getPropertyValue("receiveBufferSize"));
assertEquals(30, dfa.getPropertyValue("soReceiveBufferSize")); assertEquals(30, dfa.getPropertyValue("soReceiveBufferSize"));
assertEquals(32, dfa.getPropertyValue("soTimeout")); assertEquals(32, dfa.getPropertyValue("soTimeout"));
assertEquals(false, dfa.getPropertyValue("close"));
} }
@Test @Test
@@ -217,7 +229,6 @@ public class ParserUnitTests {
assertEquals(53, dfa.getPropertyValue("soSendBufferSize")); assertEquals(53, dfa.getPropertyValue("soSendBufferSize"));
assertEquals(54, dfa.getPropertyValue("soTimeout")); assertEquals(54, dfa.getPropertyValue("soTimeout"));
assertEquals(false, dfa.getPropertyValue("usingDirectBuffers")); assertEquals(false, dfa.getPropertyValue("usingDirectBuffers"));
} }
@Test @Test
@@ -233,7 +244,6 @@ public class ParserUnitTests {
assertEquals(53, dfa.getPropertyValue("soSendBufferSize")); assertEquals(53, dfa.getPropertyValue("soSendBufferSize"));
assertEquals(54, dfa.getPropertyValue("soTimeout")); assertEquals(54, dfa.getPropertyValue("soTimeout"));
assertEquals(true, dfa.getPropertyValue("usingDirectBuffers")); assertEquals(true, dfa.getPropertyValue("usingDirectBuffers"));
} }
@Test @Test
@@ -248,7 +258,6 @@ public class ParserUnitTests {
assertEquals(27, dfa.getPropertyValue("soTrafficClass")); assertEquals(27, dfa.getPropertyValue("soTrafficClass"));
assertEquals(53, dfa.getPropertyValue("soSendBufferSize")); assertEquals(53, dfa.getPropertyValue("soSendBufferSize"));
assertEquals(54, dfa.getPropertyValue("soTimeout")); assertEquals(54, dfa.getPropertyValue("soTimeout"));
} }
@Test @Test
@@ -267,13 +276,13 @@ public class ParserUnitTests {
assertEquals(125, dfa.getPropertyValue("soSendBufferSize")); assertEquals(125, dfa.getPropertyValue("soSendBufferSize"));
assertEquals(126, dfa.getPropertyValue("soTimeout")); assertEquals(126, dfa.getPropertyValue("soTimeout"));
assertEquals(23, dfa.getPropertyValue("poolSize")); assertEquals(23, dfa.getPropertyValue("poolSize"));
assertEquals(false, dfa.getPropertyValue("close"));
} }
@Test @Test
public void testOutGateway() { public void testOutGateway() {
DirectFieldAccessor dfa = new DirectFieldAccessor(simpleTcpNetOutboundGateway); DirectFieldAccessor dfa = new DirectFieldAccessor(simpleTcpNetOutboundGateway);
assertTrue(simpleTcpNetOutboundGateway.getPort() >= 6500); assertTrue(simpleTcpNetOutboundGateway.getPort() >= 6600);
assertEquals(MessageFormats.FORMAT_CRLF, dfa.getPropertyValue("messageFormat")); assertEquals(MessageFormats.FORMAT_CRLF, dfa.getPropertyValue("messageFormat"));
TcpNetSendingMessageHandler handler = (TcpNetSendingMessageHandler) dfa TcpNetSendingMessageHandler handler = (TcpNetSendingMessageHandler) dfa
.getPropertyValue("handler"); .getPropertyValue("handler");
@@ -284,6 +293,42 @@ public class ParserUnitTests {
assertEquals(224, dfa.getPropertyValue("soReceiveBufferSize")); assertEquals(224, dfa.getPropertyValue("soReceiveBufferSize"));
assertEquals(225, delegateDfa.getPropertyValue("soSendBufferSize")); assertEquals(225, delegateDfa.getPropertyValue("soSendBufferSize"));
assertEquals(226, delegateDfa.getPropertyValue("soTimeout")); assertEquals(226, delegateDfa.getPropertyValue("soTimeout"));
assertEquals(false, dfa.getPropertyValue("close"));
} }
@Test
public void testInGatewayClose() {
DirectFieldAccessor dfa = new DirectFieldAccessor(simpleTcpNetInboundGatewayClose);
assertTrue(simpleTcpNetInboundGatewayClose.getPort() >= 6700);
assertEquals(MessageFormats.FORMAT_CRLF, dfa.getPropertyValue("messageFormat"));
TcpNetReceivingChannelAdapter delegate = (TcpNetReceivingChannelAdapter) dfa
.getPropertyValue("delegate");
DirectFieldAccessor delegateDfa = new DirectFieldAccessor(delegate);
assertEquals(CustomNetSocketReader.class, delegateDfa.getPropertyValue("customSocketReaderClass"));
assertEquals(CustomNetSocketWriter.class, dfa.getPropertyValue("customSocketWriterClass"));
assertEquals(true, dfa.getPropertyValue("soKeepAlive"));
assertEquals(123, dfa.getPropertyValue("receiveBufferSize"));
assertEquals(124, dfa.getPropertyValue("soReceiveBufferSize"));
assertEquals(125, dfa.getPropertyValue("soSendBufferSize"));
assertEquals(126, dfa.getPropertyValue("soTimeout"));
assertEquals(23, dfa.getPropertyValue("poolSize"));
assertEquals(true, dfa.getPropertyValue("close"));
}
@Test
public void testOutGatewayClose() {
DirectFieldAccessor dfa = new DirectFieldAccessor(simpleTcpNetOutboundGatewayClose);
assertTrue(simpleTcpNetOutboundGatewayClose.getPort() >= 6800);
assertEquals(MessageFormats.FORMAT_CRLF, dfa.getPropertyValue("messageFormat"));
TcpNetSendingMessageHandler handler = (TcpNetSendingMessageHandler) dfa
.getPropertyValue("handler");
DirectFieldAccessor delegateDfa = new DirectFieldAccessor(handler);
assertEquals(CustomNetSocketReader.class, dfa.getPropertyValue("customSocketReaderClass"));
assertEquals(CustomNetSocketWriter.class, delegateDfa.getPropertyValue("customSocketWriterClass"));
assertEquals(true, delegateDfa.getPropertyValue("soKeepAlive"));
assertEquals(224, dfa.getPropertyValue("soReceiveBufferSize"));
assertEquals(225, delegateDfa.getPropertyValue("soSendBufferSize"));
assertEquals(226, delegateDfa.getPropertyValue("soTimeout"));
assertEquals(true, dfa.getPropertyValue("close"));
}
} }

View File

@@ -41,11 +41,14 @@ public class CustomNetSocketReader extends NetSocketReader {
* @see org.springframework.integration.ip.tcp.NetSocketReader#assembleDataCustomFormat() * @see org.springframework.integration.ip.tcp.NetSocketReader#assembleDataCustomFormat()
*/ */
@Override @Override
protected boolean assembleDataCustomFormat() throws IOException { protected int assembleDataCustomFormat() throws IOException {
byte[] buff = new byte[24]; byte[] buff = new byte[24];
read(buff); int status = read(buff, true);
if (status < 0) {
return status;
}
assembledData = buff; assembledData = buff;
return true; return MESSAGE_COMPLETE;
} }

View File

@@ -44,17 +44,17 @@ public class CustomNioSocketReader extends NioSocketReader {
* @see org.springframework.integration.ip.tcp.NetSocketReader#assembleDataCustomFormat() * @see org.springframework.integration.ip.tcp.NetSocketReader#assembleDataCustomFormat()
*/ */
@Override @Override
protected boolean assembleDataCustomFormat() throws IOException { protected int assembleDataCustomFormat() throws IOException {
if (buffer == null) { if (buffer == null) {
buffer = allocate(24); buffer = allocate(24);
} }
readChannel(buffer); readChannel(buffer);
if (buffer.hasRemaining()) { if (buffer.hasRemaining()) {
return false; return MESSAGE_INCOMPLETE;
} }
assembledData = buffer.array(); assembledData = buffer.array();
buffer = null; buffer = null;
return true; return MESSAGE_COMPLETE;
} }

View File

@@ -45,14 +45,14 @@ public class NetSocketReaderTests {
Socket socket = server.accept(); Socket socket = server.accept();
socket.setSoTimeout(5000); socket.setSoTimeout(5000);
NetSocketReader reader = new NetSocketReader(socket); NetSocketReader reader = new NetSocketReader(socket);
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String(reader.getAssembledData())); new String(reader.getAssembledData()));
} }
else { else {
fail("Failed to assemble first message"); fail("Failed to assemble first message");
} }
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String(reader.getAssembledData())); new String(reader.getAssembledData()));
} }
@@ -75,14 +75,14 @@ public class NetSocketReaderTests {
socket.setSoTimeout(5000); socket.setSoTimeout(5000);
NetSocketReader reader = new NetSocketReader(socket); NetSocketReader reader = new NetSocketReader(socket);
reader.setMessageFormat(MessageFormats.FORMAT_STX_ETX); reader.setMessageFormat(MessageFormats.FORMAT_STX_ETX);
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String(reader.getAssembledData())); new String(reader.getAssembledData()));
} }
else { else {
fail("Failed to assemble first message"); fail("Failed to assemble first message");
} }
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String(reader.getAssembledData())); new String(reader.getAssembledData()));
} }
@@ -105,14 +105,14 @@ public class NetSocketReaderTests {
socket.setSoTimeout(5000); socket.setSoTimeout(5000);
NetSocketReader reader = new NetSocketReader(socket); NetSocketReader reader = new NetSocketReader(socket);
reader.setMessageFormat(MessageFormats.FORMAT_CRLF); reader.setMessageFormat(MessageFormats.FORMAT_CRLF);
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String(reader.getAssembledData())); new String(reader.getAssembledData()));
} }
else { else {
fail("Failed to assemble first message"); fail("Failed to assemble first message");
} }
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String(reader.getAssembledData())); new String(reader.getAssembledData()));
} }
@@ -131,7 +131,7 @@ public class NetSocketReaderTests {
socket.setSoTimeout(5000); socket.setSoTimeout(5000);
NetSocketReader reader = new NetSocketReader(socket); NetSocketReader reader = new NetSocketReader(socket);
try { try {
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
fail("Expected message length exceeded exception"); fail("Expected message length exceeded exception");
} }
} catch (IOException e) { } catch (IOException e) {
@@ -153,7 +153,7 @@ public class NetSocketReaderTests {
NetSocketReader reader = new NetSocketReader(socket); NetSocketReader reader = new NetSocketReader(socket);
reader.setMessageFormat(MessageFormats.FORMAT_STX_ETX); reader.setMessageFormat(MessageFormats.FORMAT_STX_ETX);
try { try {
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
fail("Expected message length exceeded exception"); fail("Expected message length exceeded exception");
} }
} catch (IOException e) { } catch (IOException e) {
@@ -176,7 +176,7 @@ public class NetSocketReaderTests {
reader.setMessageFormat(MessageFormats.FORMAT_STX_ETX); reader.setMessageFormat(MessageFormats.FORMAT_STX_ETX);
reader.setMaxMessageSize(1024); reader.setMaxMessageSize(1024);
try { try {
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
fail("Expected message length exceeded exception"); fail("Expected message length exceeded exception");
} }
} catch (IOException e) { } catch (IOException e) {
@@ -198,7 +198,7 @@ public class NetSocketReaderTests {
NetSocketReader reader = new NetSocketReader(socket); NetSocketReader reader = new NetSocketReader(socket);
reader.setMessageFormat(MessageFormats.FORMAT_CRLF); reader.setMessageFormat(MessageFormats.FORMAT_CRLF);
try { try {
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
fail("Expected message length exceeded exception"); fail("Expected message length exceeded exception");
} }
} catch (IOException e) { } catch (IOException e) {
@@ -221,7 +221,7 @@ public class NetSocketReaderTests {
reader.setMessageFormat(MessageFormats.FORMAT_CRLF); reader.setMessageFormat(MessageFormats.FORMAT_CRLF);
reader.setMaxMessageSize(1024); reader.setMaxMessageSize(1024);
try { try {
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
fail("Expected message length exceeded exception"); fail("Expected message length exceeded exception");
} }
} catch (IOException e) { } catch (IOException e) {

View File

@@ -82,7 +82,7 @@ public class NioSocketReaderTests {
iterator.remove(); iterator.remove();
if (key.isReadable()) { if (key.isReadable()) {
assertEquals(channel, key.channel()); assertEquals(channel, key.channel());
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String(reader.getAssembledData())); new String(reader.getAssembledData()));
count++; count++;
@@ -138,7 +138,7 @@ public class NioSocketReaderTests {
iterator.remove(); iterator.remove();
if (key.isReadable()) { if (key.isReadable()) {
assertEquals(channel, key.channel()); assertEquals(channel, key.channel());
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
assertEquals("Data", "xx", assertEquals("Data", "xx",
new String(reader.getAssembledData())); new String(reader.getAssembledData()));
done = true; done = true;
@@ -198,7 +198,7 @@ public class NioSocketReaderTests {
iterator.remove(); iterator.remove();
if (key.isReadable()) { if (key.isReadable()) {
assertEquals(channel, key.channel()); assertEquals(channel, key.channel());
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String(reader.getAssembledData())); new String(reader.getAssembledData()));
count++; count++;
@@ -258,7 +258,7 @@ public class NioSocketReaderTests {
iterator.remove(); iterator.remove();
if (key.isReadable()) { if (key.isReadable()) {
assertEquals(channel, key.channel()); assertEquals(channel, key.channel());
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals("Data", SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String(reader.getAssembledData())); new String(reader.getAssembledData()));
count++; count++;
@@ -318,7 +318,7 @@ public class NioSocketReaderTests {
if (key.isReadable()) { if (key.isReadable()) {
assertEquals(channel, key.channel()); assertEquals(channel, key.channel());
try { try {
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
fail("Expected message length exceeded exception"); fail("Expected message length exceeded exception");
} }
} catch (IOException e) { } catch (IOException e) {
@@ -387,7 +387,7 @@ public class NioSocketReaderTests {
if (key.isReadable()) { if (key.isReadable()) {
assertEquals(channel, key.channel()); assertEquals(channel, key.channel());
try { try {
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
fail("Expected message length exceeded exception"); fail("Expected message length exceeded exception");
} }
} catch (IOException e) { } catch (IOException e) {
@@ -456,7 +456,7 @@ public class NioSocketReaderTests {
if (key.isReadable()) { if (key.isReadable()) {
assertEquals(channel, key.channel()); assertEquals(channel, key.channel());
try { try {
if (reader.assembleData()) { if (reader.assembleData() == SocketReader.MESSAGE_COMPLETE) {
fail("Expected message length exceeded exception"); fail("Expected message length exceeded exception");
} }
} catch (IOException e) { } catch (IOException e) {

View File

@@ -134,9 +134,9 @@ public class SimpleTcpNetInboundGatewayTests {
startup = 0; startup = 0;
Socket socket = SocketFactory.getDefault().createSocket("localhost", gatewayCustom.getPort()); Socket socket = SocketFactory.getDefault().createSocket("localhost", gatewayCustom.getPort());
String greetings = "Hello World!"; String greetings = "Hello World!";
String pad = " "; String pad = " ";
socket.getOutputStream().write((greetings).getBytes()); socket.getOutputStream().write((greetings).getBytes());
socket.getOutputStream().write(pad.getBytes()); // will be truncated socket.getOutputStream().write(pad.getBytes());
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
int c; int c;
int n = 0; int n = 0;

View File

@@ -15,6 +15,8 @@
*/ */
package org.springframework.integration.ip.tcp; package org.springframework.integration.ip.tcp;
import static org.junit.Assert.assertEquals;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@@ -22,7 +24,6 @@ import java.util.concurrent.TimeUnit;
import javax.net.ServerSocketFactory; import javax.net.ServerSocketFactory;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -35,8 +36,6 @@ import org.springframework.integration.message.MessageBuilder;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
/** /**
* @author Gary Russell * @author Gary Russell
* *
@@ -74,16 +73,29 @@ public class SimpleTcpNetOutboundGatewayTests {
SimpleTcpNetOutboundGateway gateway = new SimpleTcpNetOutboundGateway SimpleTcpNetOutboundGateway gateway = new SimpleTcpNetOutboundGateway
("localhost", inboundGatewayCrLf.getPort()); ("localhost", inboundGatewayCrLf.getPort());
gateway.setMessageFormat(MessageFormats.FORMAT_CRLF); gateway.setMessageFormat(MessageFormats.FORMAT_CRLF);
waitListening(inboundGatewayCrLf);
Message<String> message = MessageBuilder.withPayload("test").build(); Message<String> message = MessageBuilder.withPayload("test").build();
byte[] bytes = (byte[]) gateway.handleRequestMessage(message); byte[] bytes = (byte[]) gateway.handleRequestMessage(message);
assertEquals("echo:test", new String(bytes)); assertEquals("echo:test", new String(bytes));
} }
private void waitListening(SimpleTcpNetInboundGateway gateway) throws Exception {
int n = 0;
while (!gateway.isListening()) {
Thread.sleep(100);
if (n++ > 100) {
throw new Exception("Gateway failed to listen");
}
}
}
@Test @Test
public void testOutboundStxEtx() throws Exception { public void testOutboundStxEtx() throws Exception {
SimpleTcpNetOutboundGateway gateway = new SimpleTcpNetOutboundGateway SimpleTcpNetOutboundGateway gateway = new SimpleTcpNetOutboundGateway
("localhost", inboundGatewayStxEtx.getPort()); ("localhost", inboundGatewayStxEtx.getPort());
gateway.setMessageFormat(MessageFormats.FORMAT_STX_ETX); gateway.setMessageFormat(MessageFormats.FORMAT_STX_ETX);
waitListening(inboundGatewayStxEtx);
Message<String> message = MessageBuilder.withPayload("test").build(); Message<String> message = MessageBuilder.withPayload("test").build();
byte[] bytes = (byte[]) gateway.handleRequestMessage(message); byte[] bytes = (byte[]) gateway.handleRequestMessage(message);
assertEquals("echo:test", new String(bytes)); assertEquals("echo:test", new String(bytes));
@@ -94,6 +106,7 @@ public class SimpleTcpNetOutboundGatewayTests {
SimpleTcpNetOutboundGateway gateway = new SimpleTcpNetOutboundGateway SimpleTcpNetOutboundGateway gateway = new SimpleTcpNetOutboundGateway
("localhost", inboundGatewayLength.getPort()); ("localhost", inboundGatewayLength.getPort());
gateway.setMessageFormat(MessageFormats.FORMAT_LENGTH_HEADER); gateway.setMessageFormat(MessageFormats.FORMAT_LENGTH_HEADER);
waitListening(inboundGatewayLength);
Message<String> message = MessageBuilder.withPayload("test").build(); Message<String> message = MessageBuilder.withPayload("test").build();
byte[] bytes = (byte[]) gateway.handleRequestMessage(message); byte[] bytes = (byte[]) gateway.handleRequestMessage(message);
assertEquals("echo:test", new String(bytes)); assertEquals("echo:test", new String(bytes));
@@ -106,6 +119,7 @@ public class SimpleTcpNetOutboundGatewayTests {
gateway.setMessageFormat(MessageFormats.FORMAT_CUSTOM); gateway.setMessageFormat(MessageFormats.FORMAT_CUSTOM);
gateway.setCustomSocketReaderClassName("org.springframework.integration.ip.tcp.CustomNetSocketReader"); gateway.setCustomSocketReaderClassName("org.springframework.integration.ip.tcp.CustomNetSocketReader");
gateway.setCustomSocketWriterClassName("org.springframework.integration.ip.tcp.CustomNetSocketWriter"); gateway.setCustomSocketWriterClassName("org.springframework.integration.ip.tcp.CustomNetSocketWriter");
waitListening(inboundGatewayCustom);
Message<String> message = MessageBuilder.withPayload("test").build(); Message<String> message = MessageBuilder.withPayload("test").build();
byte[] bytes = (byte[]) gateway.handleRequestMessage(message); byte[] bytes = (byte[]) gateway.handleRequestMessage(message);
assertEquals("echo:test", new String(bytes).trim()); assertEquals("echo:test", new String(bytes).trim());
@@ -119,11 +133,12 @@ public class SimpleTcpNetOutboundGatewayTests {
assertEquals("echo:test", new String(bytes).trim()); assertEquals("echo:test", new String(bytes).trim());
} }
@Ignore @Test @Test
public void testOutboundClose() throws Exception { public void testOutboundClose() throws Exception {
final int port = SocketUtils.findAvailableServerSocket(); final int port = SocketUtils.findAvailableServerSocket();
final CountDownLatch latch1 = new CountDownLatch(1); final CountDownLatch latch1 = new CountDownLatch(1);
final CountDownLatch latch2 = new CountDownLatch(1); final CountDownLatch latch2 = new CountDownLatch(1);
final CountDownLatch latch3 = new CountDownLatch(1);
Thread t = new Thread(new Runnable() { Thread t = new Thread(new Runnable() {
public void run() { public void run() {
try { try {
@@ -134,8 +149,9 @@ public class SimpleTcpNetOutboundGatewayTests {
byte[] b = new byte[1024]; byte[] b = new byte[1024];
s.getInputStream().read(b); s.getInputStream().read(b);
s.getOutputStream().write("OK\r\n".getBytes()); s.getOutputStream().write("OK\r\n".getBytes());
s.close();
latch2.countDown(); latch2.countDown();
latch3.await();
s.close();
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -146,9 +162,11 @@ public class SimpleTcpNetOutboundGatewayTests {
SimpleTcpNetOutboundGateway gateway = new SimpleTcpNetOutboundGateway SimpleTcpNetOutboundGateway gateway = new SimpleTcpNetOutboundGateway
("localhost", port); ("localhost", port);
gateway.setMessageFormat(MessageFormats.FORMAT_CRLF); gateway.setMessageFormat(MessageFormats.FORMAT_CRLF);
gateway.setClose(true);
Message<String> message = MessageBuilder.withPayload("test").build(); Message<String> message = MessageBuilder.withPayload("test").build();
byte[] bytes = (byte[]) gateway.handleRequestMessage(message); byte[] bytes = (byte[]) gateway.handleRequestMessage(message);
assertEquals("OK", new String(bytes)); assertEquals("OK", new String(bytes));
latch3.countDown();
latch2.await(2000, TimeUnit.MILLISECONDS); latch2.await(2000, TimeUnit.MILLISECONDS);
bytes = (byte[]) gateway.handleRequestMessage(message); bytes = (byte[]) gateway.handleRequestMessage(message);
assertEquals("OK", new String(bytes)); assertEquals("OK", new String(bytes));

View File

@@ -91,8 +91,8 @@ public class SocketMessageMapperTests {
/* (non-Javadoc) /* (non-Javadoc)
* @see org.springframework.integration.ip.tcp.SocketReader#assembleData() * @see org.springframework.integration.ip.tcp.SocketReader#assembleData()
*/ */
public boolean assembleData() { public int assembleData() {
return false; return SocketReader.MESSAGE_INCOMPLETE;
} }
/* (non-Javadoc) /* (non-Javadoc)

View File

@@ -18,6 +18,8 @@ package org.springframework.integration.ip.tcp;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import java.util.concurrent.CountDownLatch;
import org.junit.Test; import org.junit.Test;
import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.Message; import org.springframework.integration.core.Message;
@@ -44,14 +46,13 @@ public class TcpReceivingChannelAdapterTests {
taskScheduler.initialize(); taskScheduler.initialize();
adapter.setTaskScheduler(taskScheduler); adapter.setTaskScheduler(taskScheduler);
adapter.start(); adapter.start();
Thread.sleep(2000); // wait for server to start listening waitListening(adapter);
SocketUtils.testSendLength(port, null); //sends 2 copies of TEST_STRING twice SocketUtils.testSendLength(port, null); //sends 2 copies of TEST_STRING twice
Thread.sleep(2000); // wait for asynch processing Message<?> message = channel.receive(2000);
Message<?> message = channel.receive(0);
assertNotNull(message); assertNotNull(message);
assertEquals(SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals(SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String((byte[])message.getPayload())); new String((byte[])message.getPayload()));
message = channel.receive(0); message = channel.receive(2000);
assertNotNull(message); assertNotNull(message);
assertEquals(SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals(SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String((byte[])message.getPayload())); new String((byte[])message.getPayload()));
@@ -74,14 +75,13 @@ public class TcpReceivingChannelAdapterTests {
taskScheduler.initialize(); taskScheduler.initialize();
adapter.setTaskScheduler(taskScheduler); adapter.setTaskScheduler(taskScheduler);
adapter.start(); adapter.start();
Thread.sleep(2000); // wait for server to start listening waitListening(adapter);
SocketUtils.testSendStxEtx(port, null); //sends 2 copies of TEST_STRING twice SocketUtils.testSendStxEtx(port, null); //sends 2 copies of TEST_STRING twice
Thread.sleep(4000); // wait for asynch processing Message<?> message = channel.receive(4000);
Message<?> message = channel.receive(0);
assertNotNull(message); assertNotNull(message);
assertEquals("\u0002" + SocketUtils.TEST_STRING + SocketUtils.TEST_STRING + "\u0003", assertEquals("\u0002" + SocketUtils.TEST_STRING + SocketUtils.TEST_STRING + "\u0003",
new String((byte[])message.getPayload())); new String((byte[])message.getPayload()));
message = channel.receive(0); message = channel.receive(2000);
assertNotNull(message); assertNotNull(message);
assertEquals("\u0002" + SocketUtils.TEST_STRING + SocketUtils.TEST_STRING + "\u0003", assertEquals("\u0002" + SocketUtils.TEST_STRING + SocketUtils.TEST_STRING + "\u0003",
new String((byte[])message.getPayload())); new String((byte[])message.getPayload()));
@@ -102,14 +102,13 @@ public class TcpReceivingChannelAdapterTests {
taskScheduler.initialize(); taskScheduler.initialize();
adapter.setTaskScheduler(taskScheduler); adapter.setTaskScheduler(taskScheduler);
adapter.start(); adapter.start();
Thread.sleep(2000); // wait for server to start listening waitListening(adapter);
SocketUtils.testSendLength(port, null); //sends 2 copies of TEST_STRING twice SocketUtils.testSendLength(port, null); //sends 2 copies of TEST_STRING twice
Thread.sleep(2000); // wait for asynch processing Message<?> message = channel.receive(2000);
Message<?> message = channel.receive(0);
assertNotNull(message); assertNotNull(message);
assertEquals(SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals(SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String((byte[])message.getPayload())); new String((byte[])message.getPayload()));
message = channel.receive(0); message = channel.receive(2000);
assertNotNull(message); assertNotNull(message);
assertEquals(SocketUtils.TEST_STRING + SocketUtils.TEST_STRING, assertEquals(SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String((byte[])message.getPayload())); new String((byte[])message.getPayload()));
@@ -131,18 +130,98 @@ public class TcpReceivingChannelAdapterTests {
taskScheduler.initialize(); taskScheduler.initialize();
adapter.setTaskScheduler(taskScheduler); adapter.setTaskScheduler(taskScheduler);
adapter.start(); adapter.start();
Thread.sleep(2000); // wait for server to start listening waitListening(adapter);
SocketUtils.testSendStxEtx(port, null); //sends 2 copies of TEST_STRING twice SocketUtils.testSendStxEtx(port, null); //sends 2 copies of TEST_STRING twice
Thread.sleep(4000); // wait for asynch processing Message<?> message = channel.receive(2000);
Message<?> message = channel.receive(0);
assertNotNull(message); assertNotNull(message);
assertEquals("\u0002" + SocketUtils.TEST_STRING + SocketUtils.TEST_STRING + "\u0003", assertEquals("\u0002" + SocketUtils.TEST_STRING + SocketUtils.TEST_STRING + "\u0003",
new String((byte[])message.getPayload())); new String((byte[])message.getPayload()));
message = channel.receive(0); message = channel.receive(2000);
assertNotNull(message); assertNotNull(message);
assertEquals("\u0002" + SocketUtils.TEST_STRING + SocketUtils.TEST_STRING + "\u0003", assertEquals("\u0002" + SocketUtils.TEST_STRING + SocketUtils.TEST_STRING + "\u0003",
new String((byte[])message.getPayload())); new String((byte[])message.getPayload()));
adapter.stop(); adapter.stop();
} }
/**
* Tests close option on inbound adapter.
*
* @throws Exception
*/
@Test
public void testNetClose() throws Exception {
QueueChannel channel = new QueueChannel(2);
int port = SocketUtils.findAvailableServerSocket();
AbstractTcpReceivingChannelAdapter adapter = new TcpNetReceivingChannelAdapter(port);
adapter.setOutputChannel(channel);
adapter.setClose(true);
adapter.setMessageFormat(MessageFormats.FORMAT_CRLF);
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.initialize();
adapter.setTaskScheduler(taskScheduler);
adapter.start();
waitListening(adapter);
CountDownLatch latch = new CountDownLatch(1);
SocketUtils.testSendCrLfSingle(port, latch);
Message<?> message = channel.receive(5000);
latch.countDown();
assertNotNull(message);
assertEquals(SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String((byte[])message.getPayload()));
latch = new CountDownLatch(1);
SocketUtils.testSendCrLfSingle(port, latch);
message = channel.receive(5000);
latch.countDown();
assertNotNull(message);
assertEquals(SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String((byte[])message.getPayload()));
adapter.stop();
}
/**
* Tests close option on inbound adapter.
*
* @throws Exception
*/
@Test
public void testNioClose() throws Exception {
QueueChannel channel = new QueueChannel(2);
int port = SocketUtils.findAvailableServerSocket();
AbstractTcpReceivingChannelAdapter adapter = new TcpNioReceivingChannelAdapter(port);
adapter.setOutputChannel(channel);
adapter.setClose(true);
adapter.setMessageFormat(MessageFormats.FORMAT_CRLF);
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.initialize();
adapter.setTaskScheduler(taskScheduler);
adapter.start();
waitListening(adapter);
CountDownLatch latch = new CountDownLatch(1);
SocketUtils.testSendCrLfSingle(port, latch);
Message<?> message = channel.receive(2000);
assertNotNull(message);
assertEquals(SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String((byte[])message.getPayload()));
latch = new CountDownLatch(1);
SocketUtils.testSendCrLfSingle(port, latch);
message = channel.receive(2000);
assertNotNull(message);
assertEquals(SocketUtils.TEST_STRING + SocketUtils.TEST_STRING,
new String((byte[])message.getPayload()));
adapter.stop();
}
private void waitListening(AbstractInternetProtocolReceivingChannelAdapter adapter) throws Exception {
int n = 0;
while (!adapter.isListening()) {
Thread.sleep(100);
if (n++ > 100) {
throw new Exception("Gateway failed to listen");
}
}
}
} }

View File

@@ -222,7 +222,34 @@ public class SocketUtils {
thread.setDaemon(true); thread.setDaemon(true);
thread.start(); thread.start();
} }
/**
* Sends a single message +CRLF.
* @param latch Waits for latch to count down before closing the socket.
*/
public static void testSendCrLfSingle(final int port, final CountDownLatch latch) {
Thread thread = new Thread(new Runnable() {
public void run() {
try {
Socket socket = new Socket(InetAddress.getByName("localhost"), port);
OutputStream outputStream = socket.getOutputStream();
outputStream.write(TEST_STRING.getBytes());
outputStream.write(TEST_STRING.getBytes());
writeByte(outputStream, '\r', true);
writeByte(outputStream, '\n', true);
if (latch != null) {
latch.await();
}
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.setDaemon(true);
thread.start();
}
/** /**
* Sends a large CRLF message with no CRLF. * Sends a large CRLF message with no CRLF.
*/ */