GH-3256: Support Testing TCP Connections

Resolves https://github.com/spring-projects/spring-integration/issues/3256
This commit is contained in:
Gary Russell
2020-04-23 15:26:50 -04:00
committed by Artem Bilan
parent 02407f7dff
commit 1d4626f54c
6 changed files with 265 additions and 37 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.integration.ip.tcp.connection;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.contains;
@@ -30,29 +31,36 @@ import static org.mockito.Mockito.when;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Rule;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.ip.config.TcpConnectionFactoryFactoryBean;
import org.springframework.integration.ip.event.IpIntegrationEvent;
import org.springframework.integration.ip.tcp.TcpOutboundGateway;
import org.springframework.integration.ip.tcp.TcpReceivingChannelAdapter;
import org.springframework.integration.test.rule.Log4j2LevelAdjuster;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.support.ErrorMessage;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@@ -65,9 +73,6 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
*/
public class ConnectionFactoryTests {
@Rule
public Log4j2LevelAdjuster adjuster = Log4j2LevelAdjuster.trace();
@Test
public void factoryBeanTests() {
TcpConnectionFactoryFactoryBean fb = new TcpConnectionFactoryFactoryBean("client");
@@ -98,23 +103,13 @@ public class ConnectionFactoryTests {
: 5; // Listening, + OPEN, CLOSE (but we *might* get exceptions, depending on timing).
final CountDownLatch serverListeningLatch = new CountDownLatch(1);
final CountDownLatch eventLatch = new CountDownLatch(expectedEvents);
ApplicationEventPublisher publisher = new ApplicationEventPublisher() {
@Override
public void publishEvent(ApplicationEvent event) {
LogFactory.getLog(this.getClass()).trace("Received: " + event);
events.add((IpIntegrationEvent) event);
if (event instanceof TcpConnectionServerListeningEvent) {
serverListeningLatch.countDown();
}
eventLatch.countDown();
ApplicationEventPublisher publisher = event -> {
LogFactory.getLog(this.getClass()).trace("Received: " + event);
events.add((IpIntegrationEvent) event);
if (event instanceof TcpConnectionServerListeningEvent) {
serverListeningLatch.countDown();
}
@Override
public void publishEvent(Object event) {
}
eventLatch.countDown();
};
serverFactory.setBeanName("serverFactory");
serverFactory.setApplicationEventPublisher(publisher);
@@ -247,6 +242,101 @@ public class ConnectionFactoryTests {
factory.stop();
}
@Test
void healthCheckSuccessNet() throws InterruptedException {
healthCheckSuccess(new TcpNetServerConnectionFactory(0), false);
}
@Test
void healthCheckSuccessNio() throws InterruptedException {
healthCheckSuccess(new TcpNioServerConnectionFactory(0), false);
}
@Test
void healthCheckFailureNet() throws InterruptedException {
healthCheckSuccess(new TcpNetServerConnectionFactory(0), true);
}
@Test
void healthCheckFailureNio() throws InterruptedException {
healthCheckSuccess(new TcpNioServerConnectionFactory(0), true);
}
private void healthCheckSuccess(AbstractServerConnectionFactory server, boolean fail) throws InterruptedException {
CountDownLatch serverUp = new CountDownLatch(1);
server.setApplicationEventPublisher(event -> {
if (event instanceof TcpConnectionServerListeningEvent) {
serverUp.countDown();
}
});
server.setBeanFactory(mock(BeanFactory.class));
AtomicReference<TcpConnection> connection = new AtomicReference<>();
server.registerSender(conn -> {
connection.set(conn);
});
AtomicInteger tested = new AtomicInteger();
server.registerListener(msg -> {
if (!(msg instanceof ErrorMessage)) {
String payload = new String((byte[]) msg.getPayload());
if (payload.equals("PING")) {
tested.incrementAndGet();
connection.get().send(new GenericMessage<>(fail ? "PANG" : "PONG"));
}
else {
connection.get().send(msg);
}
}
return false;
});
server.start();
assertThat(serverUp.await(10, TimeUnit.SECONDS)).isTrue();
TcpNetClientConnectionFactory clientFactory = new TcpNetClientConnectionFactory("localhost", server.getPort());
clientFactory.setApplicationEventPublisher(event -> { });
clientFactory.setConnectionTest(conn -> {
CountDownLatch latch = new CountDownLatch(1);
AtomicBoolean result = new AtomicBoolean();
conn.registerTestListener(msg -> {
if (Arrays.equals("PONG".getBytes(), (byte[]) msg.getPayload())) {
result.set(true);
}
latch.countDown();
return false;
});
conn.send(new GenericMessage<>("PING"));
try {
latch.await(10, TimeUnit.SECONDS);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return result.get();
});
TcpOutboundGateway gateway = new TcpOutboundGateway();
gateway.setRemoteTimeout(60000);
gateway.setConnectionFactory(clientFactory);
QueueChannel outputChannel = new QueueChannel();
gateway.setOutputChannel(outputChannel);
gateway.start();
if (fail) {
assertThatExceptionOfType(MessagingException.class).isThrownBy(() ->
gateway.handleMessage(new GenericMessage<>("test1")))
.withMessageContaining("Connection test failed for");
}
else {
gateway.handleMessage(new GenericMessage<>("test1"));
Message<?> received = outputChannel.receive(0);
assertThat(received).isNotNull();
assertThat(received.getPayload()).isEqualTo("test1".getBytes());
gateway.handleMessage(new GenericMessage<>("test2"));
received = outputChannel.receive(0);
assertThat(received).isNotNull();
assertThat(received.getPayload()).isEqualTo("test2".getBytes());
assertThat(tested.get()).isEqualTo(1);
}
gateway.stop();
server.stop();
}
@SuppressWarnings("serial")
private class FooEvent extends TcpConnectionOpenEvent {