package org.springframework.integration.samples.dynamictcp;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.EnableMessageHistory;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.context.IntegrationFlowContext;
import org.springframework.integration.dsl.context.IntegrationFlowRegistration;
import org.springframework.integration.ip.tcp.TcpReceivingChannelAdapter;
import org.springframework.integration.ip.tcp.TcpSendingMessageHandler;
import org.springframework.integration.ip.tcp.connection.TcpNetClientConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpNetServerConnectionFactory;
import org.springframework.integration.router.AbstractMessageRouter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.util.Assert;
@SpringBootApplication
@EnableMessageHistory
public class DynamicTcpClientApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DynamicTcpClientApplication.class, args);
ToTCP toTcp = context.getBean(ToTCP.class);
toTcp.send("foo", "localhost", 1234);
toTcp.send("foo", "localhost", 5678);
QueueChannel outputChannel = context.getBean("outputChannel", QueueChannel.class);
System.out.println(outputChannel.receive(10000));
System.out.println(outputChannel.receive(10000));
context.close();
}
// Client side
@MessagingGateway(defaultRequestChannel = "toTcp.input")
public interface ToTCP {
public void send(String data, @Header("host") String host, @Header("port") int port);
}
@Bean
public IntegrationFlow toTcp() {
return f -> f.route(new TcpRouter());
}
// Two servers
@Bean
public TcpNetServerConnectionFactory cfOne() {
return new TcpNetServerConnectionFactory(1234);
}
@Bean
public TcpReceivingChannelAdapter inOne(TcpNetServerConnectionFactory cfOne) {
TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
adapter.setConnectionFactory(cfOne);
adapter.setOutputChannel(outputChannel());
return adapter;
}
@Bean
public TcpNetServerConnectionFactory cfTwo() {
return new TcpNetServerConnectionFactory(5678);
}
@Bean
public TcpReceivingChannelAdapter inTwo(TcpNetServerConnectionFactory cfTwo) {
TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
adapter.setConnectionFactory(cfTwo);
adapter.setOutputChannel(outputChannel());
return adapter;
}
@Bean
public QueueChannel outputChannel() {
return new QueueChannel();
}
public static class TcpRouter extends AbstractMessageRouter {
private final static int MAX_CACHED = 10; // When this is exceeded, we remove the LRU.
@SuppressWarnings("serial")
private final LinkedHashMap<String, MessageChannel> subFlows =
new LinkedHashMap<String, MessageChannel>(MAX_CACHED, .75f, true) {
@Override
protected boolean removeEldestEntry(Entry<String, MessageChannel> eldest) {
if (size() > MAX_CACHED) {
removeSubFlow(eldest);
return true;
}
else {
return false;
}
}
};
@Autowired
private IntegrationFlowContext flowContext;
@Override
protected synchronized Collection<MessageChannel> determineTargetChannels(Message<?> message) {
MessageChannel channel = this.subFlows
.get(message.getHeaders().get("host", String.class) + message.getHeaders().get("port"));
if (channel == null) {
channel = createNewSubflow(message);
}
return Collections.singletonList(channel);
}
private MessageChannel createNewSubflow(Message<?> message) {
String host = (String) message.getHeaders().get("host");
Integer port = (Integer) message.getHeaders().get("port");
Assert.state(host != null && port != null, "host and/or port header missing");
String hostPort = host + port;
TcpNetClientConnectionFactory cf = new TcpNetClientConnectionFactory(host, port);
TcpSendingMessageHandler handler = new TcpSendingMessageHandler();
handler.setConnectionFactory(cf);
IntegrationFlow flow = f -> f.handle(handler);
IntegrationFlowRegistration flowRegistration =
this.flowContext.registration(flow)
.addBean(cf)
.id(hostPort + ".flow")
.register();
MessageChannel inputChannel = flowRegistration.getInputChannel();
this.subFlows.put(hostPort, inputChannel);
return inputChannel;
}
private void removeSubFlow(Entry<String, MessageChannel> eldest) {
String hostPort = eldest.getKey();
this.flowContext.remove(hostPort + ".flow");
}
}
}