Skip to content

Remove unnecessary error() log message from the TcpSendingMessageHandler #9937

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
artembilan opened this issue Apr 1, 2025 · 10 comments
Closed

Comments

@artembilan
Copy link
Member

More info in SO: https://stackoverflow.com/questions/79547786/how-to-silence-spring-integration-tcp-pool-timeout

@artembilan artembilan added this to the 6.5.0-RC1 milestone Apr 1, 2025
@artembilan artembilan self-assigned this Apr 1, 2025
spring-builds pushed a commit that referenced this issue Apr 1, 2025
Fixes: #9937
Issue link: #9937

The `TcpSendingMessageHandler` throws exceptions to the caller this or other way.
There is just no reason to have extra `logger.error()` in those cases

(cherry picked from commit fe5b12d)
spring-builds pushed a commit that referenced this issue Apr 1, 2025
Fixes: #9937
Issue link: #9937

The `TcpSendingMessageHandler` throws exceptions to the caller this or other way.
There is just no reason to have extra `logger.error()` in those cases

(cherry picked from commit fe5b12d)
@shirshak55
Copy link
Contributor

which version is it available? Just wanted to check

I believe only gateway receives the error from the call site, but if we are using adapter, what will happen? I believe there is no error channel on int-ip:tcp-outbound-channel-adapter

@artembilan
Copy link
Member Author

It is not available yet.
The release plan is April 22.
Feel free to subscribe into our release calendar : https://spring.io/projects#release-calendar !

The error channel on the inbound gateway is for standard messaging error handling. But that error is still an exception thrown from downstream flow . Where the mentioned outbound channel adapter is the end of integration flow. So, if you just send message over there manually, not via inbound event, then just try..catch it.

Also, see a @MessagingGateway for POJO style interaction with the flow: https://docs.spring.io/spring-integration/reference/gateway.html

@shirshak55
Copy link
Contributor

@artembilan not even snapshot one?

Gateway is easier to handle. Only issue is adapter here.

For example this one:

    <!-- TCP client connection factory (connects to TCP server at localhost:12345) -->
    <bean id="tcpClientConnectionFactory"
        class="org.springframework.integration.ip.tcp.connection.TcpNetClientConnectionFactory">
        <constructor-arg value="localhost" />
        <constructor-arg value="12345" />
    </bean>

    <!-- Wrap the connection factory with a caching client connection factory (cache size: 10) -->
    <bean id="cachingClientConnectionFactory"
        class="org.springframework.integration.ip.tcp.connection.CachingClientConnectionFactory">
        <constructor-arg ref="tcpClientConnectionFactory" />
        <constructor-arg value="3" />
    </bean>

    <!-- Outbound adapter: sends messages on the TCP connection using the caching connection factory -->
    <int-ip:tcp-outbound-channel-adapter id="tcpOutboundAdapter"
        connection-factory="cachingClientConnectionFactory"
        channel="tcpOut" />

    <!-- Inbound adapter: listens for replies on the TCP connection in client mode -->
    <int-ip:tcp-inbound-channel-adapter id="tcpInboundAdapter"
        connection-factory="cachingClientConnectionFactory"
        channel="tcpReplyChannel"
    />

    <!-- Define channels -->
    <int:channel id="tcpOut" />
    <int:channel id="tcpReplyChannel" />

    <!-- Service activator that prints any reply received -->
    <int:service-activator input-channel="tcpReplyChannel" ref="replyPrinter" method="printReply" />

    <!-- A simple bean that prints the reply message -->
    <bean id="replyPrinter" class="com.example.demo.ReplyPrinter" />

Today it throws the errror

org.springframework.integration.util.PoolItemNotAvailableException: Failed to obtain pooled item
at org.springframework.integration.util.SimplePool.getItem(SimplePool.java:213) ~[spring-integration-core-6.5.0-M3.jar:6.5.0-M3]
at org.springframework.integration.ip.tcp.connection.CachingClientConnectionFactory.obtainConnection(CachingClientConnectionFactory.java:123) ~[spring-integration-ip-6.5.0-M3.jar:6.5.0-M3]

I also want these logs to be suppressed, and wish there is better option.

@artembilan
Copy link
Member Author

Snapshots are there in the: https://repo.spring.io/snapshot.

We don’t see from your code what produces messages to that tcpOut channel. Either way that is responsibility of that part to catch an exception and retry, for example .

@shirshak55
Copy link
Contributor

@artembilan

I am just doing simple tests.

Oh sorry for not providing full code:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ImportResource;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;

@SpringBootApplication
@ImportResource("classpath:applicationContext.xml")
public class DemoApplication {

	public static void main(String[] args) {
		ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args);
		// Obtain the outbound channel defined in the XML
		MessageChannel tcpOut = ctx.getBean("tcpOut", MessageChannel.class);
		// Send a message. The remote TCP server is expected to send a reply.
		for (int i = 0; i < 10; i++) {
			tcpOut.send(MessageBuilder.withPayload("Hello TCP Server from main method " + i).build());
		}
	}
}

package com.example.demo;

public class ReplyPrinter {
    public void printReply(byte[] payload) {
        System.out.println("Received reply: " + new String(payload));
    }
}

@shirshak55
Copy link
Contributor

I checked 6.5.0-SNAPSHOT

It looks good thanks.

Exception in thread "main" org.springframework.messaging.MessageHandlingException: Failed to obtain a connection in the [bean 'org.springframework.integration.ip.tcp.TcpSendingMessageHandler#0' for component 'tcpOutboundAdapter'], failedMessage=GenericMessage [payload=Hello TCP Server from main method 0, headers={id=7ab039ce-9af3-d180-e430-3a1aea840e1d, timestamp=1743823826331}]
        at org.springframework.integration.ip.tcp.TcpSendingMessageHandler.obtainConnection(TcpSendingMessageHandler.java:93)
        at org.springframework.integration.ip.tcp.TcpSendingMessageHandler.doWrite(TcpSendingMessageHandler.java:177)
        at org.springframework.integration.ip.tcp.TcpSendingMessageHandler.handleMessageAsClient(TcpSendingMessageHandler.java:147)
        at org.springframework.integration.ip.tcp.TcpSendingMessageHandler.handleMessageInternal(TcpSendingMessageHandler.java:109)
        at org.springframework.integration.handler.AbstractMessageHandler.doHandleMessage(AbstractMessageHandler.java:105)
        at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:73)
        at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:132)
        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:72)
        at org.springframework.integration.channel.AbstractMessageChannel.sendInternal(AbstractMessageChannel.java:432)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:343)
        at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:312)
        at com.example.demo.DemoApplication.main(DemoApplication.java:20)
Caused by: org.springframework.integration.util.PoolItemNotAvailableException: Failed to obtain pooled item
        at org.springframework.integration.util.SimplePool.getItem(SimplePool.java:213)
        at org.springframework.integration.ip.tcp.connection.CachingClientConnectionFactory.obtainConnection(CachingClientConnectionFactory.java:123)
        at org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory.getConnection(AbstractClientConnectionFactory.java:120)
        at org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory.getConnection(AbstractClientConnectionFactory.java:42)
        at org.springframework.integration.ip.tcp.TcpSendingMessageHandler.obtainConnection(TcpSendingMessageHandler.java:90)
        ... 13 more
Caused by: org.springframework.messaging.MessagingException: Failed to obtain connection
        at org.springframework.integration.ip.tcp.connection.CachingClientConnectionFactory$TcpConnectionPoolItemCallback.createForPool(CachingClientConnectionFactory.java:513)
        at org.springframework.integration.ip.tcp.connection.CachingClientConnectionFactory$TcpConnectionPoolItemCallback.createForPool(CachingClientConnectionFactory.java:504)
        at org.springframework.integration.util.SimplePool.doGetItem(SimplePool.java:223)
        at org.springframework.integration.util.SimplePool.getItem(SimplePool.java:204)
        ... 17 more
Caused by: java.io.UncheckedIOException: java.net.ConnectException: Connection refused
        at org.springframework.integration.ip.tcp.connection.TcpNetClientConnectionFactory.buildNewConnection(TcpNetClientConnectionFactory.java:70)
        at org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory.doObtain(AbstractClientConnectionFactory.java:182)
        at org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory.obtainNewConnection(AbstractClientConnectionFactory.java:162)
        at org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory.obtainConnection(AbstractClientConnectionFactory.java:130)
        at org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory.getConnection(AbstractClientConnectionFactory.java:120)
        at org.springframework.integration.ip.tcp.connection.CachingClientConnectionFactory$TcpConnectionPoolItemCallback.createForPool(CachingClientConnectionFactory.java:510)
        ... 20 more
Caused by: java.net.ConnectException: Connection refused
        at java.base/sun.nio.ch.Net.pollConnect(Native Method)
        at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682)
        at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542)
        at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:592)
        at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
        at java.base/java.net.Socket.connect(Socket.java:760)
        at org.springframework.integration.ip.tcp.connection.TcpNetClientConnectionFactory.createSocket(TcpNetClientConnectionFactory.java:113)
        at org.springframework.integration.ip.tcp.connection.TcpNetClientConnectionFactory.buildNewConnection(TcpNetClientConnectionFactory.java:54)
        ... 25 more

@artembilan
Copy link
Member Author

Right. So, you do manually tcpOut.send(). Just calling Java method from another place. If that is natural to use a catch for exceptions from Java calls, that must not be different for Spring Integration as well. The error channel is for inbound channel adapters where your program doesn’t have control to catch exceptions . Not sure what kind of confusion you have yet.

Thank you for confirming about 6.5.0-SNAPSHOT!

@shirshak55
Copy link
Contributor

shirshak55 commented Apr 5, 2025

@artembilan For synchronous channel, I am good. I was just worried about asynchronous channels where error isn't thrown because it is asynchronous. But as per the docs, starting with 5.4.3, errors aren't silently ignored, so we should be good here.

I was just worried that after removing logs, we may silently ignore the error in various situation. After looking docs, I believe there is no such situation after 5.4.3.

@artembilan
Copy link
Member Author

For async channel you should use an errorChannel header in the message you send - and the framework will be able to route error over there. Actually that’s request-reply pattern is implemented in the framework anyway. But since you send yourself from the test, see if setting that header helps you to catch those errors and so on even in async case.

@shirshak55
Copy link
Contributor

@artembilan Yea, looks good. Thanks for removing the redundant logs as it was producing same error two times previously. And, I believe subscribing error channel is compulsory, so people won't miss errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants