timeout issues in spring integration gateway for soap request and reply - spring-integration

I am facing some chellenge while setting the timeout for the soap request to ESB interface from my interface(Using spring integration). While sending the request to ESB interface, I am setting the request timeout = 5000ms and reply timeout = 5000ms but in case the services is down on ESB interface, the request is not timeout in the desired timeout time of 5000ms(5 sec) and timeout sometimes in 40sec or more. I tried to use the default request timeout and default reply timeout options also with the int:gateway configuration but the same issue. Please see the below configuration done:
<int:gateway id="SoapESBGateway"
service-interface="test.soap.service.ServiceSoap">
<int:method name="ServiceResponse"
request-channel="RequestChannel" reply-channel="ReplyChannel"
request-timeout="5000" reply-timeout="5000" />
</int:gateway>
<int:chain input-channel="RequestChannel" output-channel="ReplyChannel">
<int-xml:marshalling-transformer marshaller="marshaller" result-transformer="resultTransformer"></int-xml:marshalling-transformer>
<int:transformer ref="TransformerBean" method="transformMethod"></int:transformer>
<int-ws:outbound-gateway id="ws-SoapESB-gateway" ignore-empty-responses="true" uri="${soap.URI}"></int-ws:outbound-gateway>
<int:transformer ref="TransformerBean" method="transformMethod"></int:transformer>
<int-xml:unmarshalling-transformer unmarshaller="marshaller"></int-xml:unmarshalling-transformer>
</int:chain>
<bean id="resultTransformer" class="org.springframework.integration.xml.transformer.ResultToStringTransformer" />
<bean id="TransformerBean" class="test.TransformerImpl"></bean>
Please let me know if I am missing something while setting the timeout option.
Thanks,
Vinay A
updated:
<int:gateway id="SoapESBGateway"
service-interface="test.soap.service.ServiceSoap">
<int:method name="ServiceResponse"
request-channel="RequestChannel" reply-timeout="5000" />
</int:gateway>
<int:chain input-channel="RequestChannel">
<int-xml:marshalling-transformer marshaller="marshaller" result-transformer="resultTransformer"></int-xml:marshalling-transformer>
<int:transformer ref="TransformerBean" method="transformMethod"></int:transformer>
<int-ws:outbound-gateway id="ws-SoapESB-gateway" ignore-empty-responses="true" uri="${soap.URI}" message-sender="messageSender"></int-ws:outbound-gateway>
<int:transformer ref="TransformerBean" method="transformMethod"></int:transformer>
<int-xml:unmarshalling-transformer unmarshaller="marshaller"></int-xml:unmarshalling-transformer>
</int:chain>
<bean id="resultTransformer" class="org.springframework.integration.xml.transformer.ResultToStringTransformer" />
<bean id="TransformerBean" class="test.TransformerImpl"></bean>
<bean id ="messageSender" class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<property name="connectionTimeout" value="2000"></property>
</bean>
As above, I have added messageSender bean reference with the int-ws:outbound-gateway and set the property connectiontimeout = 2sec but I can still see that this is waiting around for 19sec for first hit when network is down and for the next hits, less then 19 sec but not the desired timeout for the first hit at least. So do we not have any option where we may ascertain that the desired timeout work always.
Thanks,
Vinay

First of all, please, read this chapter: http://docs.spring.io/spring-integration/docs/latest-ga/reference/html/messaging-endpoints-chapter.html#d4e4197
According to your config you have direct flow, so there is no reason to have request-timeout and reply-channel.
The issue of your 40sec is under that WS gateway's WebServiceMessageSender. In your case it is HttpUrlConnectionMessageSender by default. And there is some connectTimeout attribute in the HttpURLConnection.
I'm not sure how we can change it, but I prefer to use HttpComponentsMessageSender to have more control over HTTP connections.
Now the question Why is it 40sec, but not those 5sec from reply-timeout?. Because your thread is blocked on the send and it hasn't reached to the wait reply part yet.

Related

Receive the acknowledgement from TCP server to our application using spring Integration

Currently we are using Spring Integration 2.1.0 Release in our application.
Application flow is as below:
Some operation is performed in application and we got the output string in String via Active MQ.
I have used message-driven-channel-adapter and service-activator to read the data from queue.
That data is displayed successfully on Server(application is working as client) using tcp-outbound-gateway.
Problem is while getting the acknowledgement from server.
Created a new channel and entered in reply-channel in tcp-outbound-gateway
Passing the same channel in service-activator as input channel.
It is showing below error:
[task-scheduler-5] 2017-10-05 18:32:20,732 ERROR org.springframework.integration.handler.LoggingHandler - org.springframework.integration.MessageDeliveryException: Dispatcher has no subscribers.
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:108)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:101)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:61)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:157)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:128)
at org.springframework.integration.core.MessagingTemplate.doSend(MessagingTemplate.java:288)
at org.springframework.integration.core.MessagingTemplate.send(MessagingTemplate.java:149)
Code is as below
<context:property-placeholder />
<!-- Gateway and connection factory setting -->
<int:channel id="telnetLandingChannel" />
<int:channel id="telnetReplyChannel" />
<beans:bean id="clientCustomSerializer"
class="com.telnet.core.serializer.CustomSerializer">
<beans:property name="terminatingChar" value="10" />
<beans:property name="maxLength" value="65535" />
</beans:bean>
<int:gateway id="gw" default-reply-channel="telnetReplyChannel" default-reply-timeout="100000"
service-interface="com.telnet.core.integration.connection.ParseTcpConfiguration$SimpleGateway"
default-request-channel="telnetLandingChannel"/>
<ip:tcp-connection-factory id="clientFactory"
type="client" host="localhost" port="7777" single-use="false" using-nio="false"
serializer="${client.serializer}" deserializer="${client.serializer}" />
<ip:tcp-outbound-gateway id="clientInGw"
request-channel="telnetLandingChannel"
connection-factory="clientFactory"
reply-channel="telnetReplyChannel"
reply-timeout="100000"/>
<!-- To send the messege over server via JMS and serviceActivator -->
<int:channel id="incidentDispatchMessageChannel" />
<int:channel id="jmsChannel" />
<beans:bean id="customClientServiceActivator"
class= "com.telnet.core.integration.CustomClientServiceActivator">
</beans:bean>
<int-jms:message-driven-channel-adapter id="incidentDispatchMessageChannelAdapter" error-channel="errorChannel"
connection-factory="mqConnectionFactory"
destination-name="${incident.processing.messaging.dispatch.queues}"
channel="incidentDispatchMessageChannel"/>
<int:service-activator id="incidentMessageActivator"
input-channel="incidentDispatchMessageChannel"
output-channel="jmsChannel"
ref="customClientServiceActivator" method="getOutboundMessage">
</int:service-activator>
<int:object-to-string-transformer id="clientBytes2String"
input-channel="jmsChannel"
output-channel="telnetLandingChannel"/>
<!-- To receive the acknowledgement message on server via serviceActivator -->
<int:service-activator id="incidentAck"
input-channel="telnetReplyChannel"
ref="customClientServiceActivator" method="getAck">
</int:service-activator>
I have studied various article on stackverFlow but not able to get any solution
Yeah... That isn't clear by the error what channel is guilty.
On the other hand you really use very old Spring Integration version.
Would be great to consider to upgrade to the latest: http://projects.spring.io/spring-integration/.
However I think that issue is somehow around exactly that reply-channel, which you use not only for the <service-activator> but for the <int:gateway> as well.
I suggest you to remove default-reply-channel="telnetReplyChannel" from the gateway definition, remove reply-channel="telnetReplyChannel" from the <ip:tcp-outbound-gateway> definition. And let them communicate via replyChannel header populated by the gateway during request.
Regarding your <int-jms:message-driven-channel-adapter> flow which leads to the same <ip:tcp-outbound-gateway>, I would suggest to still stay with the replyChannel header but here populate it via <header-enricher> before sending message to the telnetLandingChannel. That replyChannel via <header-enricher> would be exactly an input-channel for the subsequent <int:service-activator> to handle ack from the <ip:tcp-outbound-gateway>.
I got the solution of this issue, there are multiple xmls in our code but i have added the code in one to show flow in stackOverflow.
Issue was i had defined in a xml which has only configuration part like Outbound adapter connection factory where as it should be defined in another xml where I am using service activator. Changed the place of channel definition and it worked.
I want to disconnect the TCP (as server) the moment i got the response message. As of now I am using so-timeout, so my TCP server will gets timedout after the time given in so-timeout, but requirement is to disconnect the connection the moment TCP print/display the acknowledgement. Please suggest how can I implement this.

Spring-Integration Default Retry Configuration

I have a spring-integration project which does the following
1.) Read messages from a queue
2.) Transform messages
3.) Send transformed messages to an Api
Relevant Config for Step 1
<bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="MQConnectionFactory" />
<property name="sessionCacheSize" value="10"/>
</bean>
<bean id="requestQueue" class="com.ibm.mq.jms.MQQueue">
<constructor-arg index="0" value="${queuemanager}"/>
<constructor-arg index="1" value="${incoming.queue}"/>
</bean>
<integration:poller id="poller" default="true" fixed-delay="1000"/>
<jms:message-driven-channel-adapter id="jmsIn"
destination="requestQueue"
channel="inputJsonConversionChannel"
connection-factory="cachingConnectionFactory" />
Step 3 is a Service Activator, and in case of a failure (not 201 HTTP status) I am throwing a custom exception.
Relevant config for step 3
<int:service-activator input-channel="ApiChannel" ref="EventApiClient" method="post"/>
<int:service-activator input-channel="errorChannel" ref="PListenerExceptionHandler" method="handleFailure"/>
The behaviour that occurs is that, it keeps trying to connect and gets the same errors over and over again.
I wanted to know if someone could explain to me
how is this default retry being configured/triggered?
how can I redirect the errors to an error channel, because right now errors in Step 1 use the global error channel and the default error handler I created. But errors from the Service Activator are not.
Cheers
Kris
It's retrying because the message-driven channel adapter is transactional by default, which means the exception causes the message to be rolled back onto the queue.
You can add an error-channel to the adapter and any exceptions will be sent in the form of an ErrorMessage which has a payload MessagingException which has properties failedMessage and cause.
If the integration flow downstream of the error channel "consumes" the error, the transaction will be committed and the message removed. If the error flow throws an exception, the transaction will be rolled back, as before.
There is a default errorChannel which just logs the exception by default.
error-channel="errorChannel"
or you can use a custom channel and put your own logic in a subscriber to that channel.

Spring Integration AMQP - retries continuously despite retry advice

I've used Spring Integration with JMS very successfully before, but we're now using with RabbitMQ / AMQP and having some issues with error handling.
I have an int-amqp:inbound-channel-adapter with with a errorChannel set up to receive any exception, here an ErrorTransformer class inspects the failed Message's cause exception. Then depending on the type of exception either :-
suppresses the exception and transforms into a JSON object that can go to AMQP outbound-channel-adapter as a business reply explaining the failure. Here I want the original message consumed/ACKed.
Or Re-throws the causing exception to let RabbitMQ re-deliver the message, a certain number of times.
I found that re-throwing caused the message to be re-delivered continuously, I then read about StatefulRetryOperationsInterceptorFactoryBean, so added an advice chain to retry 3 times, then I got exception about no message-id, so also added a 'MissingMessageIdAdvice' at the start of the advice chain.
Despite the advice, I still get continuous re-tries for a RuntimeException that is re-thrown from errorChannel's ErrorTransformer. I'm publishing the message via RabbitMQ admin just using the defaults. Not sure if the lack of a message-id is making this not work, if so how do I get a message to have an id ?
I'm confused about the differences between the :-
A) ConditionalRejectingErrorHandler (I've set as the inbound adapter's error-handler) which allows me to provide a customFatalExceptionStrategy to implement isFatal(). Where I believe fatal=true (means DISCARD) and message is consumed and discarded, but how can I still send an outbound failure message ?
B) And the errorChannel I have on the inbound adapter which I'm using to inspect an Exception and transform into a outbound fail response message. Here I guess I could throw AmqpRejectAndDontRequeueException, but then why have the ConditionalRejectingErrorHandler as well ? and will thorwing AmqpRejectAndDontRequeueException work
<int-amqp:inbound-channel-adapter id="amqpInRequestPatternValuation" channel="requestAmqpIn" channel-transacted="true" transaction-manager="transactionManager"
queue-names="requestQueue" error-channel="patternValuationErrorChannel" connection-factory="connectionFactory"
receive-timeout="59000" concurrent-consumers="1"
advice-chain="retryChain" error-handler="customErrorHandler" />
<bean id="customErrorHandler" class="org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler">
<constructor-arg ref="customFatalExceptionStrategy"/>
</bean>
<bean id="customFatalExceptionStrategy" class="abc.common.CustomFatalExceptionStrategy"/>
<!-- ADVICE CHAIN FOR CONTROLLING NUMBER OF RE-TRIES before sending to DLQ (or discarding if no DLQ) without this any re-queued fatal message will retry forever -->
<util:list id="retryChain">
<bean class="org.springframework.amqp.rabbit.retry.MissingMessageIdAdvice">
<constructor-arg>
<bean class="org.springframework.retry.policy.MapRetryContextCache" />
</constructor-arg>
</bean>
<ref bean="retryInterceptor" />
</util:list>
<bean id="retryInterceptor"
class="org.springframework.amqp.rabbit.config.StatefulRetryOperationsInterceptorFactoryBean">
<property name="retryOperations" ref="retryTemplate" />
<property name="messageRecoverer" ref="messageRecoverer"/>
</bean>
<bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
<property name="retryPolicy" ref="simpleRetryPolicy" />
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="10000" />
</bean>
</property>
</bean>
<bean id="simpleRetryPolicy" class="org.springframework.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="3" />
</bean>
You have to use RejectAndDontRequeueRecoverer to stop re-delivery in the end of retries:
* MessageRecover that causes the listener container to reject
* the message without requeuing. This enables failed messages
* to be sent to a Dead Letter Exchange/Queue, if the broker is
* so configured.
Yes, the messageId is important for that retry use-case.
You can inject custom MessageKeyGenerator strategy to determine a unique key from message if you can't supply it manually during sending.
I never got around to posting the solution, so here it is.
Once I had configured the retry advice chain to the AMQP inbound channel adapter which must include a messageRecoverer of RejectAndDontRequeueRecoverer (which I believe is also the dafault). The important point I was missing was to ensure that when sender's send a message they include a message_id. So if publishing via the RabbitMQ Admin Console I needed to include the predefined message_id property and supply a value.
Using the 'MissingMessageIdAdvice' doesn't help (so I removed) as it will generate a different message_id on each re-deliver on the message leading to the re-try count not incrementing as each delivery was considered different from the last

how to add a retry advice with jms:message-driven-channel-adapter

I am new to spring integration. My requirement is that if there is a connection problem to the jms q then it should try to connect 3 times then log it and exit the process. I am not able to do it. It throws an error saying it needs the ref attribute for service:activator. But I don't have/know reference of which class to provide here. Is there any other way of doing it?
<int-jms:message-driven-channel-adapter id="msgIn" channel="toRoute" container="messageListenerContainer" />
<int:service-activator id="service" input-channel="toRoute" >
<int:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
<property name="recoveryCallback">
<bean class="org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer">
<constructor-arg ref=“errorChannel" />
</bean>
</property>
</bean>
</request-handler-advice-chain>
</int:service-activator>
You seem to have completely misunderstood what the framework does.
The service-activator gets a message when one is received from JMS (which implies the connection is good), and needs "something" (a reference to a bean or an expression) to invoke as a result of receiving that message.
The retry advice is to retry calling that service if it fails to process the message for some reason. It is unrelated to whatever is the source of the message (JMS in this case).
It's not clear why you are trying to use Spring Integration for something as simple as testing whether a JMS broker is available.
Perhaps if you can provide some larger context someone might be able to help.

Spring integration-Service activator getting called twice

Hi this is my spring integration configuration..When i hit my service using mozilla fire fox rest console two times its getting called.For example if i hit some service in service-activator (CA request activator),On certaion un predicatable scenarios it s getting called twice.I dont know whetther it is mozilla issue or configuration issue.I tried using new window but this problem persists.In first case my service activator returning xml response properly but immediately it s getting called again.Only on rare scenarios its calling twice.
<int:channel id="accountRequest" />
<int:channel id="accountResponse" />
<int:channel id="catRequestChannel" />
<int:channel id="mataccountRequest" />
<int:channel id="errorChannel"/>
<int-http:inbound-gateway id="cwebAccountManagementGateway"
supported-methods="GET, POST"
request-channel="accountRequest"
reply-channel="accountResponse"
mapped-request-headers="*"
mapped-response-headers="*"
view-name="/policies"
path="/services/{class}/{method}"
reply-timeout="50000"
error-channel="errorChannel">
<int-http:header name="serviceClass" expression="#pathVariables.class"/>
<int-http:header name="serviceMethod" expression="#pathVariables.method"/>
</int-http:inbound-gateway>
<int:header-value-router input-channel="accountRequest"
header-name="state"
default-output-channel="accountRequest" resolution-required="false">
<int:mapping value="MA"
channel="mataccountRequest" />
<int:mapping value="CA"
channel="catRequestChannel" />
</int:header-value-router>
<int:service-activator id="accountServiceActivator"
input-channel="mataccountRequest"
output-channel="accountResponse"
ref="serviceGatewayAdapter"
method="requestHandler"
send-timeout="60000"/>
<int:service-activator id="caRequestActivator"
input-channel="catRequestChannel"
output-channel="accountResponse"
ref="caServiceGatewayAdapter"
method="requestHandler"
send-timeout="60000"/>
<int:service-activator id="errorRequestActivator"
input-channel="errorChannel"
output-channel="accountResponse"
ref="errorGatewayAdapter"
method="errorHandler"
send-timeout="60000"/>
for eg:This is my url
http://localhost:9085/springintegrationsample/create?mail=15999999#mail.com&idNumber=80010600010
if i edit the mail to some other values it ll get called twice
if i change agan it s working fine.I dont understand on what scenarios its getting called twice
You are using the same "requestHandler" for both of your service activators, so its obvious it will be called twice.
You can't know which activator is called for the handler method as when you debug you can only check for the handler method being called.
To better handle this, use different a handler method for each of your service activators. Even if they are using similar operations, it will be more clear and easy to debug.
I would suggest to add channel interceptors for logging of incoming and outgoing messages just to get a better idea.
cheers

Resources