Spring Integration TCP factory error handling - spring

I would like to handle TCP connection factory exceptions.
With an abstract connection factory:
#Bean
public AbstractClientConnectionFactory clientFactory() {
TcpNetClientConnectionFactory factory = new TcpNetClientConnectionFactory(host, Integer.parseInt(port));
factory.setSoKeepAlive(Boolean.parseBoolean(keepAlive));
factory.setSoTimeout(timeout);
factory.setSoReceiveBufferSize(Integer.parseInt(bufferSize));
factory.setSoSendBufferSize(Integer.parseInt(bufferSize));
return factory;
}
This connection is injected in both TcpSendingMessageHandler and TcpReceivingChannelAdapter separated beans.
#Bean
public TcpReceivingChannelAdapter tcpIn() {
...
receiver.setConnectionFactory(clientFactory());
...
}
#Bean
#ServiceActivator(...)
public TcpSendingMessageHandler tcpOut() {
...
sender.setConnectionFactory(clientFactory());
...
}
I have some ApplicatioListeners such as: TcpConnectionExceptionEvent, TcpConnectionCloseEvent and TcpConnectionOpenEvent.
#EventListener
public void handleTcpConnectionCloseEvent(TcpConnectionExceptionEvent event){
...
}
However, I detected when an opened connection was closed, a TcpConnectionExceptionEvent was launched but not when the connection was not even opened. How it is possible to deal with “connection refused” problems or any other TCP errors?
Is it possible to start/stop the connection using the control bus? I am sending:
Message operation = MessageBuilder.withPayload("#clientFactory.isRunning()").build();
boolean sent = operationChannel.send(operation);
This seems to be not working because no response was received and only creates “continuous” calls looking for the reference swamping the application. clientFactory beans exists (checked with context.getBeanDefinitionNames())
Additionally, could I set a max retries for trying to reconnect?
Edit (Retry Advice): I added the tcpRetryAdvice to my tcp outbound channel but I am still confused because the clientFactory() (when “connection refused”) does not follow the defined policy in the retryAdvice. How can I control the current attemps and if finally the message was delivered?
#Bean
#ServiceActivator(inputChannel = "tcpSender", adviceChain = "tcpRetryAdvice")
public TcpSendingMessageHandler tcpOut(AbstractClientConnectionFactory connectionFactory) { ... }
#Bean
public RequestHandlerRetryAdvice tcpRetryAdvice() {
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(2);
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(3000);
backOffPolicy.setMaxInterval(10000);
backOffPolicy.setMultiplier(2);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(retryPolicy);
retryTemplate.setBackOffPolicy(backOffPolicy);
RequestHandlerRetryAdvice tcpRetryAdvice = new RequestHandlerRetryAdvice();
tcpRetryAdvice.setRetryTemplate(retryTemplate);
// This allows fail-controlling
tcpRetryAdvice.setRecoveryCallback(new ErrorMessageSendingRecoverer(failMessageChannel()));
return tcpRetryAdvice;
}
Edit (ControlBus):
I am just trying to stop sending messages to TCP because I have to know when the TCP (connectionFactory) is connected in order to do not drop away JMS consumed messages.
Edit (Logging error for beans)
First of all, I have:
tcpRetryAdvice.setRecoveryCallback(new ErrorMessageSendingRecoverer(failMessageChannel()));
This is OK to trace the exception but how I could get the message not sent?
Then I have error sent to the errorChannel but I am still seeing the whole stacktrace when the clientFactory() bean raises a "connecton refused" exception. I would like to avoid this:
[ERROR][TcpSendingMessageHandler] - [TcpSendingMessageHandler.java:80] - 26/08/2016
20:40:57.424 - Error creating connection
java.net.ConnectException: Connection refused: connect
at java.net.TwoStacksPlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at java.net.Socket.connect(Socket.java:538)
at java.net.Socket.<init>(Socket.java:434)
at java.net.Socket.<init>(Socket.java:211)
....
I just want:
In my error channel: Message not delivered <Failed to obtain a connection; nested exception is java.net.ConnectException: Connection refused: connect>
I am sending the callback to a failChannel() and I do not want to see the stacktrace. Ideally, I want to manage the callback. Get the message not sent to save it for waiting the socket reconnection and log the error with an errorChannel.

A new TcpConnectionFailedEvent was recently added.
4.3.2 should be released in the next week or so; you can try it out first using the 4.3.2.BUILD-SNAPSHOT version.
You can add a retry advice to the outbound adapter.
I am not sure what you're saying about the control bus.

Related

Many Threads in "wait" when using RestTemplate

I'm having a problem with slowiness when many request come to my website, It starts to generate "Wait" threads, I've set up the rest template as a Bean
#Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder
.setConnectTimeout(Integer.parseInt(env.getProperty("service.configuration.http.http-request-timeout")))
.setReadTimeout(Integer.parseInt(env.getProperty("service.configuration.http.http-request-timeout")))
.requestFactory(clientHttpRequestFactory())
.build();
}
When i look for the process which is generating that problem i find HttpClient in wait.
Anybody knows what can i do to solve this problem?
I'm using java8, apache tomcat, spring boot
In my past project I used this kind of configuration:
#Bean
public RestTemplate restTemplate()
{
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setHttpClient(httpClient());
RestTemplate result = new RestTemplate(factory);
return result;
}
#Bean
public HttpClient httpClient()
{
CloseableHttpClient httpClient = null;
//Use a connection pool
PoolingHttpClientConnectionManager pcm = new PoolingHttpClientConnectionManager();
HttpClientBuilder hcb = HttpClientBuilder.create();
//Close Idle connection after 5 seconds
pcm.closeIdleConnections(5000, TimeUnit.MILLISECONDS);
//Specify all the timeouts in milli-seconds
RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(5000).setSocketTimeout(5000).setConnectTimeout(5000).build();
hcb.setDefaultRequestConfig(config);
hcb.setConnectionManager(pcm).setConnectionManagerShared(true);
// Check if proxy is required to connect to the final resource
if (proxyEnable)
{
//If enabled.... configure it
BasicCredentialsProvider credentialProvider = new BasicCredentialsProvider();
AuthScope scope = new AuthScope(hostProxy, portProxy);
if( StringUtils.hasText(usernameProxy) && StringUtils.hasText(passwordProxy) )
{
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(usernameProxy, passwordProxy);
credentialProvider.setCredentials(scope, credentials);
}
hcb.setDefaultCredentialsProvider(credentialProvider).setRoutePlanner(proxyRoutePlanner);
}
//Use custom keepalive strategy
if (cas != null)
{
hcb.setKeepAliveStrategy(cas);
}
httpClient = hcb.build();
return httpClient;
}
Where cas is an instance of:
public class WsKeepAliveStrategy implements ConnectionKeepAliveStrategy
{
private Long timeout;
#Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context)
{
return timeout;
}
public void setTimeout(Long timeout)
{
this.timeout = timeout;
}
}
In this way I could configure httpclient in order to use a connection pool, specify when to close the idle connection and specify the socket timeout, connection timeout, connection request timeout
By using this configuration I add no more issue
I hope it can be useful
Angelo
Must be a case of missing timeout, should try to get the exact problem happening in your case, and change the setting causing that. Changing RequestFactory to another library may or may not solve that it all depends on the problem - so my advise is to identify it first.
For example:
We faced similar issue that our thread was getting stuck in restTemplate so we took a thread dump which were like
"pool-12-thread-1" #41 prio=5 os_prio=0 tid=0x00007f17a624e000 nid=0x3d runnable [0x00007f1738f96000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:170)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
at sun.security.ssl.InputRecord.read(InputRecord.java:503)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
- locked <0x00000000ebc7d888> (a java.lang.Object)
at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:930)
at sun.security.ssl.AppInputStream.read(AppInputStream.java:105)
- locked <0x00000000ebc7d8a0> (a sun.security.ssl.AppInputStream)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
- locked <0x00000000e94b9608> (a java.io.BufferedInputStream)
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:704)
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1569)
- locked <0x00000000d57e5a30> (a sun.net.www.protocol.https.DelegateHttpsURLConnection)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
- locked <0x00000000d57e5a30> (a sun.net.www.protocol.https.DelegateHttpsURLConnection)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:84)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:652)
It clearly show that the reason is missing timeout in read so we added
final SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setReadTimeout(10_000); // 10 sec as needed by us
final RestTemplate restTemplate = new RestTemplate(requestFactory);
Similarly after you got the reason, add proper timeout
HERE explains why it happens.
This configuration is inline with another Baeldung's article about rest template builder. It seems nice and clean but it hides a default PoolingHttpClientConnectionManager with the defaultMaxPerRoute set to 5.
What does this default max per route means? It means that only 5 simultaneous HTTP connections to the same host will be possible.
So you can configure RestTemplate to use a pooled implementation such as HttpComponentsClientHttpRequestFactory with overridden defaultMaxPerRoute:
PoolingHttpClientConnectionManager poolingConnManager = new
PoolingHttpClientConnectionManager();
poolingConnManager.setMaxTotal(50);
poolingConnManager.setDefaultMaxPerRoute(50);

Why is Spring JMS creating a JMS connection every second when connecting to an ActiveMQ Broker?

I've created a Spring JMS application using version 4.1.2.RELEASE, which is connected to a broker that is running ActiveMQ 5.11.0. The problem that I'm seeing is as follows. In the logs, I notice that every second, I'm seeing a connection being created as such.
2017-06-21 13:10:21,046 | level=INFO | thread=ActiveMQ Task-1 | class=org.apache.activemq.transport.failover.FailoverTransport | Successfully connected to tcp://localhost:61616
I know that it is creating a new ActiveMQ connection each time, because it says successfully "connected" and not "reconnected" as shown in the code located here: http://grepcode.com/file/repo1.maven.org/maven2/com.ning/metrics.collector/1.3.3/org/apache/activemq/transport/failover/FailoverTransport.java#891
I don't have a caching connection factory set for my consumer, but I'm wondering if the following is the culprit when it comes to why I'm seeing constant connections being created.
factory.setCacheLevel(DefaultMessageListenerContainer.CACHE_NONE);
The following post states that consumers should not be cached, but I wonder if that applies to caching the connection + session. If the connection is cached, but the session is not, then I wonder if that creates a problem.
Why DefaultMessageListenerContainer should not use CachingConnectionFactory?
The following are the configurations that I'm using in my application. I am hoping that it is something that I've misconfigured, and would appreciate any insights that anyone has to offer.
Spring Configurations
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() throws Throwable {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setCacheLevel(DefaultMessageListenerContainer.CACHE_NONE);
factory.setMaxMessagesPerTask(-1);
factory.setConcurrency(1);
factory.setSessionTransacted(true);
return factory;
}
#Bean
public CachingConnectionFactory cachingConnectionFactory(){
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(connectionFactory());
cachingConnectionFactory.setCacheConsumers(false);
cachingConnectionFactory.setSessionCacheSize(1);
return cachingConnectionFactory;
}
#Bean
public ActiveMQConnectionFactory connectionFactory(){
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setInitialRedeliveryDelay(1000L);
redeliveryPolicy.setRedeliveryDelay(1000L);
redeliveryPolicy.setMaximumRedeliveries(6);
redeliveryPolicy.setUseExponentialBackOff(true);
redeliveryPolicy.setBackOffMultiplier(5);
ActiveMQConnectionFactory activeMQ = new ActiveMQConnectionFactory("admin", "admin", "tcp://localhost:61616");
activeMQ.setRedeliveryPolicy(redeliveryPolicy);
activeMQ.setPrefetchPolicy(prefetchPolicy());
return activeMQ;
}
#Bean
public JmsMessagingTemplate jmsMessagingTemplate(){
ActiveMQTopic activeMQ = new ActiveMQTopic("topic.out");
JmsMessagingTemplate jmsMessagingTemplate = new JmsMessagingTemplate(cachingConnectionFactory());
jmsMessagingTemplate.setDefaultDestination(activeMQ);
return jmsMessagingTemplate;
}
protected ActiveMQPrefetchPolicy prefetchPolicy(){
ActiveMQPrefetchPolicy prefetchPolicy = new ActiveMQPrefetchPolicy();
int prefetchValue = 1000;
prefetchPolicy.setQueuePrefetch(prefetchValue);
return prefetchPolicy;
}
Thanks,
Juan
The issue was indeed the following code.
factory.setCacheLevel(DefaultMessageListenerContainer.CACHE_NONE);
The moment that I removed it, the rapid connection creation stopped.

No mannaged connection when using jms

I am using wildfly jms queue... I'm using wildfly 9.0.2.Final
I make the producer like this :
#Inject
private JMSContext jmsContext;
private JMSProducer jmsProducer;
#Resource(mappedName = "java:/jboss/exported/jms/queue/TosDownloadReport")
private Queue queueDownloadReport;
public void downloadReport(String adminId, DownloadReportFilter filter){
try {
jmsProducer = jmsContext.createProducer();
String requestParam = Json.getInstance().getObjectMapper().writeValueAsString(filter);
LOG.info("requestParam {}", requestParam);
String id = UUID.randomUUID().toString().replace("-", "");
RoutingRequest request = new RoutingRequest();
request.putProperty("id", id);
request.putProperty("adminId", adminId);
request.putProperty("parameterRequest", requestParam);
QueueMsgDownloadReport message = new QueueMsgDownloadReport();
message.setId(id);
message.setAdminId(adminId);
String jsonMsg = Json.getInstance().getObjectMapper().writeValueAsString(message);
gatewayService.send(RESOURCE, METHOD, request);
jmsProducer.send(queueDownloadReport, jsonMsg);
} catch (Exception e) {
LOG.error(e.getMessage(),e);
}
}
But sometimes i get exception like this and i must restart wildfly
2017-05-05 11:08:20,004 ERROR [com.daksa.tos.infrastructure.api.TosTimer] (EJB default - 7) Could not create a session: IJ000453: Unable to get managed connection for java:/JmsXA: javax.jms.JMSRuntimeException: Could not create a session: IJ000453: Unable to get managed connection for java:/JmsXA
Caused by: javax.resource.ResourceException: IJ000655: No managed connections available within configured blocking timeout (30000 [ms])
From what i read, i don't need to call jmsContext.close() if i'm using inject right?
Please tell me what i do wrong...Thx

How to set proxy to ActiveMQConnectionFactory to send messages

I am using spring activemq for sending jms messages to activemq message queue.
The messagequeue is created on a remote server.I have to set a proxy to reach to that message queue server from my local machine.
Can anyone help me on how to add proxy to activemqConnectionFactory class.
I tried using HttpClientTransport, ProxyConnector, NetworkConnector classes where I set the broker URL, but I am not sure how to add those to the activemqconnectionFactory.
Please find the details below:
message.queue.broker.url=ssl://<HOSTNAME>:61617
HttpClientTransport httpClientTransport = new HttpClientTransport(null, new URI(brokerUrl));
httpClientTransport.setProxyHost("<proxyHost>");
httpClientTransport.setProxyPort(3128);
httpClientTransport.start();
ProxyConnector proxy = new ProxyConnector();
proxy.setBind(new URI(brokerUrl));
proxy.setRemote(new URI(brokerUrl));
proxy.start();
NetworkConnector netConnector = new NetworkConnector() {
};
netConnector.setBrokerURL(brokerUrl);
netConnector.start();
ActiveMQConnectionFactory activeMQConnectionfactory = new ActiveMQConnectionFactory(userName, password, brokerUrl);
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(activeMQConnectionfactory);
cachingConnectionFactory.setSessionCacheSize(50);
ActiveMQQueue activeMqQueue = new ActiveMQQueue(destination);
jmsTemplate = new JmsTemplate(sslConnectionFactory);
jmsTemplate.setDefaultDestination(activeMqQueue);
#Override
public void sendMessage(final String text) {
// TODO Auto-generated method stub
this.jmsTemplate.send(new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
Message message = session.createTextMessage(text);
return message;
}
});
}
When I run the above code, I get ConnectionTimedOutException:
Caused by: javax.jms.JMSException: Could not connect to broker URL: ssl://:61617. Reason: java.net.SocketTimeoutException: connect timed out
I also tried with
ssl://<URL>?transport.proxyHost=<PROXYHOST>amp;transport.proxyPort=3128
Thanks in Advance.

IBM.XMS ExceptionListener not firing

I am using IBM.XMS 2.0.0.5 and MQ Client 7.0.1.5 and have created a connection, set the exception listener, started the connection and started listening for messages using a message listener. This works fine, except that the ExceptionListener does not fire when I disable the network connection. Instead I get an unhandled socket exception.
I have gone back to the example given on the IBM site and recreated the error:
If I disable the network connetion I get the unhandled socket exception and the exceptionListener does not get fired.
using System;
using System.Threading;
using IBM.XMS;
public class Sample
{
public static void Main()
{
XMSFactoryFactory factoryFactory = XMSFactoryFactory.GetInstance(XMSC.CT_WMQ);
IConnectionFactory connectionFactory = factoryFactory.CreateConnectionFactory();
connectionFactory.SetStringProperty(XMSC.WMQ_HOST_NAME, "**********");
connectionFactory.SetStringProperty(XMSC.WMQ_CHANNEL, "*****");
connectionFactory.SetIntProperty(XMSC.WMQ_PORT, 1414);
connectionFactory.SetIntProperty(XMSC.WMQ_CONNECTION_MODE, XMSC.WMQ_CM_CLIENT);
connectionFactory.SetStringProperty(XMSC.WMQ_QUEUE_MANAGER, "*********");
//
// Create the connection and register an exception listener
//
IConnection connection = connectionFactory.CreateConnection();
connection.ExceptionListener = new ExceptionListener(OnException);
ISession session = connection.CreateSession(false, AcknowledgeMode.AutoAcknowledge);
IDestination queue = session.CreateQueue("queue://***********");
//
// Create the consumer and register an async message listener
//
IMessageConsumer consumer = session.CreateConsumer(queue);
consumer.MessageListener = new MessageListener(OnMessage);
connection.Start();
while (true)
{
Console.WriteLine("Waiting for messages....");
Thread.Sleep(1000);
}
}
static void OnMessage(IMessage msg)
{
Console.WriteLine(msg);
}
static void OnException(Exception ex)
{
Console.WriteLine(ex);
}
}
I'll put the answer up in the hope that it saves someone the time it wasted for me.
You need to use an unmanaged client connection in order to connect up an exception listener to your connection.
connectionFactory.SetIntProperty(XMSC.WMQ_CONNECTION_MODE, XMSC.WMQ_CM_CLIENT_UNMANAGED);
If it's just worked in Unmanaged mode, then it's a bug at XMS .NET v2.0.0.5. It should work in Managed mode also (XMSC.WMQ_CM_CLIENT_MANAGED). Please move to the latest fix pack and test again.

Resources