/*
* Copyright 2002-2015 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.connection;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.integration.ip.util.TestingUtilities;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.support.LongRunningIntegrationTest;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.ErrorMessage;
/**
* @author Gary Russell
* @since 2.2
*/
public class ConnectionTimeoutTests {
@Rule
public LongRunningIntegrationTest longTests = new LongRunningIntegrationTest();
@Test
public void testDefaultTimeout() throws Exception {
TcpNetServerConnectionFactory server = new TcpNetServerConnectionFactory(0);
this.setupServerCallbacks(server, 0);
server.start();
TestingUtilities.waitListening(server, null);
TcpNetClientConnectionFactory client = new TcpNetClientConnectionFactory("localhost", server.getPort());
setupClientCallback(client);
client.start();
TcpConnection connection = client.getConnection();
Socket socket = TestUtils.getPropertyValue(connection, "socket", Socket.class);
// should default to 0 (infinite) timeout
assertEquals(0, socket.getSoTimeout());
connection.close();
server.stop();
client.stop();
}
@Test
public void testNetSimpleTimeout() throws Exception {
TcpNetServerConnectionFactory server = new TcpNetServerConnectionFactory(0);
this.setupServerCallbacks(server, 0);
server.start();
TestingUtilities.waitListening(server, null);
TcpNetClientConnectionFactory client = new TcpNetClientConnectionFactory("localhost", server.getPort());
client.registerListener(message -> false);
client.setSoTimeout(1000);
CountDownLatch clientCloseLatch = getCloseLatch(client);
setupClientCallback(client);
client.start();
TcpConnection connection = client.getConnection();
Socket socket = TestUtils.getPropertyValue(connection, "socket", Socket.class);
assertEquals(1000, socket.getSoTimeout());
assertTrue(clientCloseLatch.await(3, TimeUnit.SECONDS));
assertFalse(connection.isOpen());
server.stop();
client.stop();
}
/**
* Ensure we don't timeout on the read side (client) if we sent a message within the
* current timeout.
* @throws Exception
*/
@Test
public void testNetReplyNotTimeout() throws Exception {
TcpNetServerConnectionFactory server = new TcpNetServerConnectionFactory(0);
setupAndStartServer(server);
TcpNetClientConnectionFactory client = new TcpNetClientConnectionFactory("localhost", server.getPort());
this.notTimeoutGuts(server, client);
}
/**
* Ensure we don't timeout on the read side (client) if we sent a message within the
* current timeout.
* @throws Exception
*/
@Test
public void testNioReplyNotTimeout() throws Exception {
TcpNetServerConnectionFactory server = new TcpNetServerConnectionFactory(0);
setupAndStartServer(server);
TcpNioClientConnectionFactory client = new TcpNioClientConnectionFactory("localhost", server.getPort());
this.notTimeoutGuts(server, client);
}
private void notTimeoutGuts(AbstractServerConnectionFactory server, AbstractClientConnectionFactory client)
throws Exception, InterruptedException {
final AtomicReference<Message<?>> reply = new AtomicReference<Message<?>>();
final CountDownLatch replyLatch = new CountDownLatch(1);
client.registerListener(message -> {
if (!(message instanceof ErrorMessage)) {
reply.set(message);
replyLatch.countDown();
}
return false;
});
client.setSoTimeout(2000);
CountDownLatch clientClosedLatch = getCloseLatch(client);
setupClientCallback(client);
client.start();
TcpConnection connection = client.getConnection();
Thread.sleep(1000);
connection.send(MessageBuilder.withPayload("foo").build());
assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
assertNotNull(reply.get());
assertTrue(clientClosedLatch.await(10, TimeUnit.SECONDS));
assertFalse(connection.isOpen());
server.stop();
client.stop();
}
private void setupAndStartServer(AbstractServerConnectionFactory server) {
this.setupServerCallbacks(server, 1200);
server.start();
TestingUtilities.waitListening(server, null);
}
/**
* Ensure we do timeout on the read side (client) if we sent a message within the
* first timeout but the reply takes > 2 timeouts.
* @throws Exception
*/
@Test
public void testNetReplyTimeout() throws Exception {
TcpNetServerConnectionFactory server = new TcpNetServerConnectionFactory(0);
this.setupServerCallbacks(server, 4500);
final AtomicReference<Message<?>> reply = new AtomicReference<Message<?>>();
server.start();
TestingUtilities.waitListening(server, null);
TcpNetClientConnectionFactory client = new TcpNetClientConnectionFactory("localhost", server.getPort());
client.registerListener(message -> {
if (!(message instanceof ErrorMessage)) {
reply.set(message);
}
return false;
});
client.setSoTimeout(2000);
CountDownLatch clientCloseLatch = getCloseLatch(client);
setupClientCallback(client);
client.start();
TcpConnection connection = client.getConnection();
Socket socket = TestUtils.getPropertyValue(connection, "socket", Socket.class);
assertEquals(2000, socket.getSoTimeout());
Thread.sleep(1000);
connection.send(MessageBuilder.withPayload("foo").build());
Thread.sleep(1400);
assertTrue(connection.isOpen());
assertTrue(clientCloseLatch.await(2000, TimeUnit.SECONDS));
assertNull(reply.get());
assertFalse(connection.isOpen());
server.stop();
client.stop();
}
/**
* Ensure we do timeout on the read side (client) if we sent a message within the
* first timeout but the reply takes > 2 timeouts.
* @throws Exception
*/
@Test
public void testNioReplyTimeout() throws Exception {
TcpNetServerConnectionFactory server = new TcpNetServerConnectionFactory(0);
this.setupServerCallbacks(server, 2100);
final AtomicReference<Message<?>> reply = new AtomicReference<Message<?>>();
server.start();
TestingUtilities.waitListening(server, null);
TcpNioClientConnectionFactory client = new TcpNioClientConnectionFactory("localhost", server.getPort());
client.registerListener(message -> {
if (!(message instanceof ErrorMessage)) {
reply.set(message);
}
return false;
});
client.setSoTimeout(1000);
CountDownLatch clientCloseLatch = getCloseLatch(client);
setupClientCallback(client);
client.start();
TcpConnection connection = client.getConnection();
Thread.sleep(500);
connection.send(MessageBuilder.withPayload("foo").build());
Thread.sleep(700);
assertTrue(connection.isOpen());
assertTrue(clientCloseLatch.await(2, TimeUnit.SECONDS));
assertNull(reply.get());
assertFalse(connection.isOpen());
server.stop();
client.stop();
}
private void setupServerCallbacks(AbstractServerConnectionFactory server, final int serverDelay) {
server.setComponentName("serverFactory");
final AtomicReference<TcpConnection> serverConnection = new AtomicReference<TcpConnection>();
server.registerListener(message -> {
try {
Thread.sleep(serverDelay);
serverConnection.get().send(message);
}
catch (Exception e) {
e.printStackTrace();
}
return false;
});
server.registerSender(new TcpSender() {
@Override
public void addNewConnection(TcpConnection connection) {
serverConnection.set(connection);
}
@Override
public void removeDeadConnection(TcpConnection connection) {
}
});
}
public void setupClientCallback(AbstractClientConnectionFactory client) {
client.setComponentName("clientFactory");
client.registerSender(new TcpSender() {
@Override
public void addNewConnection(TcpConnection connection) {
}
@Override
public void removeDeadConnection(TcpConnection connection) {
}
});
}
private CountDownLatch getCloseLatch(AbstractClientConnectionFactory client) {
final CountDownLatch clientClosedLatch;
clientClosedLatch = new CountDownLatch(1);
client.setApplicationEventPublisher(new ApplicationEventPublisher() {
@Override
public void publishEvent(ApplicationEvent event) {
if (event instanceof TcpConnectionCloseEvent) {
clientClosedLatch.countDown();
}
}
@Override
public void publishEvent(Object event) {
}
});
return clientClosedLatch;
}
}