Spring Integration : Failed to send reply after netcat's command - tcp

My use case :
Client netcat to send tcp socket to the server
echo -n "coincoincoin" | nc -v host port
Server tcp with Spring Integration receive the request from client
Sever receiving the request with TcpInboundGateway, process
Server send back the reply to the client through TcpInboundGateway
The client receives and display the answer.
Unfortunately, the step from one to 3 is OK.
The step 4 is not OK and I get a
... Failed to send reply ...
The server config's file :
#Configuration
public class ServerConfig {
#Value("${server.port}")
private int port;
#Bean
TcpNetServerConnectionFactory serverConnexion() {
TcpNetServerConnectionFactory factory = new TcpNetServerConnectionFactory(port);
factory.setSingleUse(true);
factory.setDeserializer(new CustomSerializerDeserializer());
factory.setSerializer(new CustomSerializerDeserializer());
factory.setSoTimeout(10000);
return factory;
}
#Bean
TcpInboundGateway tcpGateway() {
TcpInboundGateway gateway = new TcpInboundGateway();
gateway.setConnectionFactory(serverConnexion());
gateway.setRequestChannel(reqChannel());
gateway.setReplyChannel(reqChannel());
return gateway;
}
#Bean
public MessageChannel reqChannel() {
return new DirectChannel();
}
}
Here is my exception :
2022-12-18 14:21:51.524 DEBUG 14816 --- [pool-3-thread-2] org.springframework.integration.channel.DirectChannel : [( - )] postSend (sent=true) on channel 'bean 'reqChannel'; defined in: 'class path resource [com/server/config/ServerConfig.class]'; from source: 'com.rss.server.config.ServerConfig.reqChannel()'', message: GenericMessage [payload=byte[40], headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#68980b5b, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#68980b5b, ip_tcp_remotePort=38567, ip_connectionId=kubernetes.docker.internal:38567:11000:e143aeae-532c-4aab-9ef1-ba7930ecc3ef, ip_localInetAddress=0.0.0.0/0.0.0.0, ip_address=127.0.0.1, id=3265835d-8088-7820-d7e5-0dd46ddab69c, ip_hostname=kubernetes.docker.internal, timestamp=1671369710212}]
2022-12-18 14:21:51.524 ERROR 14816 --- [pool-3-thread-2] org.springframework.integration.ip.tcp.TcpInboundGateway : [( - )] Failed to send reply
org.springframework.messaging.MessagingException: Send Failed; nested exception is java.net.SocketException: Socket is closed
at org.springframework.integration.ip.tcp.connection.TcpNetConnection.send(TcpNetConnection.java:119) ~[spring-integration-ip-5.5.8.jar:5.5.8]
at org.springframework.integration.ip.tcp.TcpInboundGateway.doOnMessage(TcpInboundGateway.java:139) [spring-integration-ip-5.5.8.jar:5.5.8]
at org.springframework.integration.ip.tcp.TcpInboundGateway.onMessage(TcpInboundGateway.java:101) [spring-integration-ip-5.5.8.jar:5.5.8]
at org.springframework.integration.ip.tcp.connection.TcpNetConnection.receiveAndProcessMessage(TcpNetConnection.java:224) [spring-integration-ip-5.5.8.jar:5.5.8]
at org.springframework.integration.ip.tcp.connection.TcpNetConnection.run(TcpNetConnection.java:197) [spring-integration-ip-5.5.8.jar:5.5.8]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
at java.lang.Thread.run(Thread.java:834) [?:?]
Caused by: java.net.SocketException: Socket is closed
at java.net.Socket.getSendBufferSize(Socket.java:1215) ~[?:?]
at org.springframework.integration.ip.tcp.connection.TcpNetConnection.send(TcpNetConnection.java:108) ~[spring-integration-ip-5.5.8.jar:5.5.8]
... 7 more
How can I solve this problem ?
Any help would be welcome! Thank you very much!

It looks like piping the echo into nc closes the socket before the response is sent; this works fine for me...
#Component
class ServerConfig {
private final int port = 1234;
#Bean
TcpNetServerConnectionFactory serverConnexion() {
TcpNetServerConnectionFactory factory = new TcpNetServerConnectionFactory(port);
factory.setSingleUse(true);
// factory.setDeserializer(new CustomSerializerDeserializer());
// factory.setSerializer(new CustomSerializerDeserializer());
factory.setSoTimeout(10000);
return factory;
}
#Bean
TcpInboundGateway tcpGateway(TcpNetServerConnectionFactory serverConnexion, MessageChannel reqChannel) {
TcpInboundGateway gateway = new TcpInboundGateway();
gateway.setConnectionFactory(serverConnexion);
gateway.setRequestChannel(reqChannel);
gateway.setReplyChannel(reqChannel);
return gateway;
}
#Bean
public MessageChannel reqChannel() {
return new DirectChannel();
}
}
Note that I am using the default (de)serializer (CRLF).
% nc -cv localhost 1234
Connection to localhost port 1234 [tcp/search-agent] succeeded!
foo
foo

Related

SignalR core console client not receiving notifications

I have looked around at some of the Similar questions and didn't figure this problem out. I have a simple Hub in my .NET core Web API project. Here is the Hub:
public class NotificationHub : Hub<INotificationClient>
{
public async Task SendMessage(string user, string msg)
{
await Clients.All.ReceiveMessage(user, msg);
}
public Task SendMessageToCaller(string msg)
{
return Clients.Caller.ReceiveMessage(msg);
}
public Task SendMessageToPartner(string user, string msg)
{
return Clients.Client(user).ReceiveMessageToPartner(msg);
}
}
Here is the Interface:
public interface INotificationClient
{
Task ReceiveMessage(string user, string msg);
Task ReceiveMessage(string msg);
Task ReceiveMessageToPartner( string msg);
}
Here is the code from the controller:
[Route("[controller]")]
[ApiController]
public class NotificationsController : ControllerBase
{
private IHubContext<NotificationHub> _hub;
public NotificationsController(IHubContext<NotificationHub> hub)
{
_hub = hub;
}
[HttpGet]
public async Task<IActionResult> Get()
{
var msg = new NotificationData { ClientId = "12345", Notification = "Somone just connected" };
await _hub.Clients.All.SendAsync("Notification", msg);
return Ok(new { Message = "Request complete" });
}
}
Lastly here is the console client code:
Console.WriteLine("Press a key to start listening");
Console.ReadKey();
Console.WriteLine("Client Listening!");
var connection = new HubConnectionBuilder()
.WithUrl("http://localhost:61514/notifications")
.Build();
connection.On<NotificationData>("Notification", (notificationData) =>
Console.WriteLine($"Somebody connected: {notificationData.ClientId}"));
connection.StartAsync().GetAwaiter().GetResult();
Console.WriteLine("Listening. Press a key to quit");
Console.ReadKey();
Here is the startup of the web app with the mappings:
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<NotificationHub>("/Notifications");
});
}
I keep getting this error: System.IO.IOException: 'The server disconnected before the handshake could be started.' I must be missing something along the way here.
Update:
Turned on the logs and got this error:
dbug: Microsoft.AspNetCore.Http.Connections.Client.HttpConnection[8]
Establishing connection with server at 'http://localhost:61514/notifications'.
dbug: Microsoft.AspNetCore.Http.Connections.Client.HttpConnection[9]
Established connection 'BdROAEEQnGUeDYAW5EspRA' with the server.
dbug: Microsoft.AspNetCore.Http.Connections.Client.HttpConnection[7]
Starting transport 'ServerSentEvents' with Url: http://localhost:61514/notifications.
info: Microsoft.AspNetCore.Http.Connections.Client.Internal.ServerSentEventsTransport[1]
Starting transport. Transfer mode: Text.
dbug: Microsoft.AspNetCore.Http.Connections.Client.Internal.ServerSentEventsTransport[3]
Starting receive loop.
dbug: Microsoft.AspNetCore.Http.Connections.Client.Internal.ServerSentEventsTransport[9]
Received 30 bytes. Parsing SSE frame.
dbug: Microsoft.AspNetCore.Http.Connections.Client.Internal.ServerSentEventsTransport[4]
Receive loop stopped.
dbug: Microsoft.AspNetCore.Http.Connections.Client.Internal.ServerSentEventsTransport[100]
Starting the send loop.
dbug: Microsoft.AspNetCore.Http.Connections.Client.HttpConnection[18]
Transport 'ServerSentEvents' started.
dbug: Microsoft.AspNetCore.Http.Connections.Client.Internal.ServerSentEventsTransport[102]
Send loop canceled.
dbug: Microsoft.AspNetCore.Http.Connections.Client.Internal.ServerSentEventsTransport[101]
Send loop stopped.
Unhandled exception. info: Microsoft.AspNetCore.Http.Connections.Client.HttpConnection[3]
HttpConnection Started.
info: Microsoft.AspNetCore.SignalR.Client.HubConnection[24]
Using HubProtocol 'json v1'.
System.IO.IOException: The server disconnected before the handshake could be started.
at Microsoft.AspNetCore.SignalR.Client.HubConnection.HandshakeAsync(ConnectionState startingConnectionState, CancellationToken cancellationToken)
at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncCore(CancellationToken cancellationToken)
at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncCore(CancellationToken cancellationToken)
at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncInner(CancellationToken cancellationToken)
at System.Threading.Tasks.ForceAsyncAwaiter.GetResult()
at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsync(CancellationToken cancellationToken)
at NotificationClient.Program.Main(String[] args)
So after chonking on this all day, I found out what the problem was, so I thought I'd post the solution here just in case someone else is having this problem.
The Hub Endpoint was pointing to the controller. So when the client was linking it was hitting the controller twice causing a the server to close the connection. So I changed this line:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<NotificationHub>("/Notify");
});
from "/Notifications" which was hitting the controller, to "Notify" as in the code above, re-pointed the client from Notifications to Notify
var connection = new HubConnectionBuilder()
.WithUrl("http://localhost:61514/Notify")
.ConfigureLogging(logging =>
{
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Debug);
})
.Build();
and messages started to flow in.

How can I write message from server to an IP/Port using Netty 3.8 ServerBootstrap (TCP)

I want to send a message from server to an IP/Port but it's not working when I write the channel. I'm not sure what I'm doing wrong.
private Channel channel;
private void createConnection() {
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
this.bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
#Override
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new SimpleChannelHandler());
}
});
bootstrap.setOption("localAddress", new InetSocketAddress("localhost", 0));
bootstrap.setOption("reuseAddress", true);
bootstrap.setOption("child.sendBufferSize", 65536);
bootstrap.setOption("child.receiveBufferSize", 65536);
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
this.channel = bootstrap.bind();
}
I'm writing a component using apache camel. When my component proccess, I want it sends a message to an IP/Port.
public void send(Exchange exchange) {
String ip = exchange.getIn().getHeader("ip", String.class);
int port = exchange.getIn().getHeader("port", Integer.class);
Object msg = exchange.getIn().getBody();
InetSocketAddress inetSocketAddress = new InetSocketAddress(ip, port);
this.channel.write(msg, inetSocketAddress);
}
I'm getting a "getUnsupportedOperationFuture". I'm not sure what I'm doing wrong. I'm using Netty 3.8 and I think the connection is set correctly. Any help?

ASP.NET controller gets called repeatedly by firefox if HttpStatusCodeResult has newlines in the description [duplicate]

When I return a StatusDescription with a newline using the HttpStatusCodeResult from ASP.Net MVC 3.0, the connection to my client is forcibly closed. App is hosted in IIS 7.0.
Example controller:
public class FooController : Controller
{
public ActionResult MyAction()
{
return new HttpStatusCodeResult((int)HttpStatusCode.BadRequest, "Foo \n Bar");
}
}
Example client:
using (WebClient client = new WebClient())
{
client.DownloadString("http://localhost/app/Foo/MyAction");
}
Thrown Exception:
System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a receive.
System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
The behavior is consistent when using curl (curl 7.25.0 (i386-pc-win32) libcurl/7.25.0 zlib/1.2.6)
curl http://localhost/app/Foo/MyAction
curl: (56) Recv failure: Connection was reset
Edit
I ended up using this custom ActionResult to get the desired results.
public class BadRequestResult : ActionResult
{
private const int BadRequestCode = (int)HttpStatusCode.BadRequest;
private int count = 0;
public BadRequestResult(string errors)
: this(errors, "")
{
}
public BadRequestResult(string format, params object[] args)
{
if (String.IsNullOrEmpty(format))
{
throw new ArgumentException("format");
}
Errors = String.Format(format, args);
count = Errors.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Length;
}
public string Errors { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
response.TrySkipIisCustomErrors = true;
response.StatusCode = BadRequestCode;
response.StatusDescription = String.Format("Bad Request {0} Error(s)", count);
response.Write(Errors);
response.End();
}
}
You can't have a linebreak in the middle of an HTTP header.
The HTTP protocol specifies the end of a header is a line break.
Since the line break is in the middle of a header, the header is not a valid header and you are getting this error.
Fix: Don't put a line break in the middle of an HTTP header.

How to solve "failed: Error during WebSocket handshake: Unexpected response code: 200" in spring websockets?

I am trying to subscribe to a particular channel in spring web sockets but while pointing to a URL using SockJs i getting the following error WebSocket connection to 'ws://localhost:8080/Spring/rest/user/chat/045/jmfz3b3j/websocket' failed: Error during WebSocket handshake: Unexpected response code: 200 Please help me to avoid this.
Here is my client side code for subscription.
Index.jsp
var stompClient =null;
function subscribe(){
var socket = new SockJS('/Spring/rest/user/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/messages', function(test) {
alert("in call back function");});
});
}
#Kane This is my websocket configuration in spring-servlet.xml
<websocket:message-broker application-destination-prefix="/app" >
<websocket:stomp-endpoint path="/chat" allowed-origins="*">
<websocket:sockjs />
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic"/>
</websocket:message-broker>
And this is my controller code
#Controller
#RequestMapping("user")
public class OptnCptController{
#MessageMapping("topic/messages")
public String getMsg(String s)
{
return s;
}
}

Spring Integration tcp client multiple connections

I use Spring Integration tcp-outbound-adapter and tcp-inbound-adapter in order to communicate with a third party external system through TCP.
The connection factory I use is of type "client" and has single-use="false", because the nature of communication with the external system is a session of several dozens requests and replies.
The external system expects I will open a new TCP connection for each session.
Is there any way to do that with Spring Integration?
My code uses SI successfully for one such session. But I want my system to open several such connections so I can handle several concurrent sessions.
Currently, if I send a message of a new session to the inbound adapter, it uses the same TCP connection.
Please help.
UPDATE:
While using the ThreadAffinity solution given by Gary here, we get this exception when we do more than 4 concurrent requests. Any idea why is that?
11:08:02.083 [pool-1-thread-2] 193.xxx.yyy.zz:443:55729:46c71372-5933-4707-a27b-93cc4bf78c59 Message sent GenericMessage [payload=byte[326], headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#2fb866, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#2fb866, ip_tcp_remotePort=55718, ip_connectionId=127.0.0.1:55718:4444:7f71ce96-eaac-4b21-8b2c-bf736102f818, ip_localInetAddress=/127.0.0.1, ip_address=127.0.0.1, id=2dc3e330-d703-8a61-c46c-012233cadf6f, ip_hostname=127.0.0.1, timestamp=1481706480700}]
11:08:12.093 [pool-1-thread-2] Remote Timeout on 193.xxx.yyy.zz:443:55729:46c71372-5933-4707-a27b-93cc4bf78c59
11:08:12.093 [pool-1-thread-2] Tcp Gateway exception
org.springframework.integration.MessageTimeoutException: Timed out waiting for response
at org.springframework.integration.ip.tcp.TcpOutboundGateway.handleRequestMessage(TcpOutboundGateway.java:146)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:109)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:373)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:292)
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:212)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:129)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:115)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at org.springframework.messaging.core.GenericMessagingTemplate.doSendAndReceive(GenericMessagingTemplate.java:150)
at org.springframework.messaging.core.GenericMessagingTemplate.doSendAndReceive(GenericMessagingTemplate.java:45)
at org.springframework.messaging.core.AbstractMessagingTemplate.sendAndReceive(AbstractMessagingTemplate.java:42)
at org.springframework.integration.core.MessagingTemplate.sendAndReceive(MessagingTemplate.java:97)
at org.springframework.integration.gateway.MessagingGatewaySupport.doSendAndReceive(MessagingGatewaySupport.java:441)
at org.springframework.integration.gateway.MessagingGatewaySupport.sendAndReceiveMessage(MessagingGatewaySupport.java:409)
at org.springframework.integration.ip.tcp.TcpInboundGateway.doOnMessage(TcpInboundGateway.java:120)
at org.springframework.integration.ip.tcp.TcpInboundGateway.onMessage(TcpInboundGateway.java:98)
at org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorSupport.onMessage(TcpConnectionInterceptorSupport.java:159)
at org.springframework.integration.ip.tcp.connection.TcpNetConnection.run(TcpNetConnection.java:182)
at org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorSupport.run(TcpConnectionInterceptorSupport.java:111)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
It depends on what constitutes a "session" - if all the requests from a session on the client side all run on a single thread, you could write a simple wrapper for the connection factory that stores the connection in a ThreadLocal. You would need some mechanism to call the factory wrapper after the last request to close the connection and remove it from the ThreadLocal.
If the requests for a session can occur on multiple threads, it would be a bit more complicated but you could still do it with a ThreadLocal that maps to a connection instance.
EDIT
Here's an example...
#SpringBootApplication
public class So40507731Application {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(So40507731Application.class, args);
MessageChannel channel = context.getBean("clientFlow.input", MessageChannel.class);
MessagingTemplate template = new MessagingTemplate(channel);
ThreadAffinityClientConnectionFactory affinityCF = context.getBean(ThreadAffinityClientConnectionFactory.class);
ExecutorService exec = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(2);
exec.execute(() -> {
String result = new String(template.convertSendAndReceive("foo", byte[].class));
System.out.println(Thread.currentThread().getName() + " " + result);
result = new String(template.convertSendAndReceive("foo", byte[].class));
System.out.println(Thread.currentThread().getName() + " " + result);
affinityCF.release();
latch.countDown();
});
exec.execute(() -> {
String result = new String(template.convertSendAndReceive("foo", byte[].class));
System.out.println(Thread.currentThread().getName() + " " + result);
result = new String(template.convertSendAndReceive("foo", byte[].class));
System.out.println(Thread.currentThread().getName() + " " + result);
affinityCF.release();
latch.countDown();
});
latch.await(10, TimeUnit.SECONDS);
context.close();
exec.shutdownNow();
}
#Bean
public TcpNetClientConnectionFactory delegateCF() {
TcpNetClientConnectionFactory clientCF = new TcpNetClientConnectionFactory("localhost", 1234);
clientCF.setSingleUse(true); // so each thread gets his own connection
return clientCF;
}
#Bean
public ThreadAffinityClientConnectionFactory affinityCF() {
return new ThreadAffinityClientConnectionFactory(delegateCF());
}
#Bean
public TcpOutboundGateway outGate() {
TcpOutboundGateway outGate = new TcpOutboundGateway();
outGate.setConnectionFactory(affinityCF());
return outGate;
}
#Bean
public IntegrationFlow clientFlow() {
return f -> f.handle(outGate());
}
#Bean
public TcpNetServerConnectionFactory serverCF() {
return new TcpNetServerConnectionFactory(1234);
}
#Bean
public TcpInboundGateway inGate() {
TcpInboundGateway inGate = new TcpInboundGateway();
inGate.setConnectionFactory(serverCF());
return inGate;
}
#Bean
public IntegrationFlow serverFlow() {
return IntegrationFlows.from(inGate())
.transform(Transformers.objectToString())
.transform("headers['ip_connectionId'] + ' ' + payload")
.get();
}
public static class ThreadAffinityClientConnectionFactory extends AbstractClientConnectionFactory
implements TcpListener {
private final AbstractClientConnectionFactory delegate;
private final ThreadLocal<TcpConnectionSupport> connection = new ThreadLocal<>();
public ThreadAffinityClientConnectionFactory(AbstractClientConnectionFactory delegate) {
super("", 0);
delegate.registerListener(this);
this.delegate = delegate;
}
#Override
protected TcpConnectionSupport obtainConnection() throws Exception {
TcpConnectionSupport tcpConnection = this.connection.get();
if (tcpConnection == null || !tcpConnection.isOpen()) {
tcpConnection = this.delegate.getConnection();
this.connection.set(tcpConnection);
}
return tcpConnection;
}
public void release() {
TcpConnectionSupport connection = this.connection.get();
if (connection != null) {
connection.close();
this.connection.remove();
}
}
#Override
public void start() {
this.delegate.start();
setActive(true);
super.start();
}
#Override
public void stop() {
this.delegate.stop();
setActive(false);
super.stop();
}
#Override
public boolean onMessage(Message<?> message) {
return getListener().onMessage(message);
}
}
}
Result:
pool-2-thread-2 localhost:64559:1234:3d898822-ea91-421d-97f2-5f9620b9d369 foo
pool-2-thread-1 localhost:64560:1234:227f8a9f-1461-41bf-943c-68a56f708b0c foo
pool-2-thread-2 localhost:64559:1234:3d898822-ea91-421d-97f2-5f9620b9d369 foo
pool-2-thread-1 localhost:64560:1234:227f8a9f-1461-41bf-943c-68a56f708b0c foo

Resources