Spring Integration is not triggering int-http:outbound-gateway URL - spring-integration

I have the following spring integration configuration which starts from reading a file from drive, transforming it into java object and sending http GET request to a REST APT. The first 2 steps are working fine in my chain, but when it comes to int-http:outbound-gateway step, the URL is never triggered nor it is displaying any error message. The application remains running state without showing any error message. It never goes to kbbCvsReadResponseTransformer class where I can check the response received from this REST service. What could be the reason. I am using spring-integration-4.1.2
<int-file:inbound-channel-adapter id="kbbFileInbound"
channel="kbbInboundFileChannel"
directory="file:/ftpguest/kbb-gm-rem/data"
filename-pattern="GM_Remarketing_Pricing_Res_*.csv"
auto-startup="true"
prevent-duplicates="true">
<int:poller fixed-rate="5000"/>
</int-file:inbound-channel-adapter>
<int:chain input-channel="kbbInboundFileChannel" output-channel="kbbCvsReadRequest">
<int:transformer ref="kbbInputFileDataTransformer" />
</int:chain>
<int:chain input-channel="kbbCvsReadRequest" output-channel="cvsVehicleReadRequest">
<int:transformer ref="kbbCvsUpdateRequestTransformer" />
</int:chain>
<int:chain input-channel="cvsVehicleReadRequest" output-channel="cvsVehicleReadResponse">
<int:header-enricher>
<int:header name="Content-Type" expression="'application/json'" />
</int:header-enricher>
<int-http:outbound-gateway http-method="GET" expected-response-type="com.fasterxml.jackson.databind.JsonNode"
charset="UTF-8" request-factory="clientHttpRequestFactory" url="http://services.dev-sea.cobaltgroup.com/inventoryWebApp/rest/v1.0/vehicles/search?vin={vin};inventoryOwner={inventoryOwner}">
<int-http:uri-variable name="vin" expression="payload.getVin()"/>
<int-http:uri-variable name="inventoryOwner" expression="payload.getInventoryOwner()"/>
<int-http:request-handler-advice-chain>
<ref bean="retrier" />
</int-http:request-handler-advice-chain>
</int-http:outbound-gateway>
</int:chain>
<int:chain input-channel="cvsVehicleReadResponse">
<int:transformer ref="kbbCvsReadResponseTransformer" />
</int:chain>
<bean id="clientHttpRequestFactory"
class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<property name="httpClient" value="#{httpComponentsMessageSender.httpClient}" />
</bean>
<bean id="kbbInputFileDataTransformer" class="com.cdk.dmg.kbb.integration.transformer.KbbInputFileDataTransformer"/>
<bean id="kbbCvsUpdateRequestTransformer" class="com.cdk.dmg.kbb.integration.transformer.KbbCVSUpdateRequestTransformer" />
<bean id="kbbCvsReadResponseTransformer" class="com.cdk.dmg.kbb.integration.transformer.KbbCVSReadResponseTransformer" />
<!-- Need to use it from integration-context.xml, there are lot of issues with this file right now, so temporerly copied it here -->
<int:handler-retry-advice id="retrier" max-attempts="5" recovery-channel="errorChannel">
<int:exponential-back-off initial="1000" multiplier="5.0" maximum="60000" />
</int:handler-retry-advice>

We can't help you here without any logs confirmation.
Please, share DEBUG logs for the org.springframework.integration to have a picture how your messages are traveling.
From other side looks like you have some error there and just send it to the errorChannel according to the <int:handler-retry-advice> configuration.
And that's why you don't get any reply to the cvsVehicleReadResponse after that...

Related

processing multiple http outbound gateways

I am working on the spring integration http. Wanted to make multiple http calls and collect the response to one common java object. I am facing an issues which says no output/reply channel at the end of the aggregator.
This is the xml definition
<int:channel id="intermediateWChannel">
<int:interceptors>
<int:wire-tap channel="intermediateWLogger" />
</int:interceptors>
</int:channel>
<int:channel id="intermediateSChannel">
<int:interceptors>
<int:wire-tap channel="intermediateSLogger" />
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="intermediateWLogger" expression="'Fetch Wtms Trip : '.concat(payload)" level="INFO" />
<int:logging-channel-adapter id="intermediateSLogger" level="INFO" />
<int:chain input-channel="intermediateSChannel" output-channel="publishsubscribechannel">
<int-http:outbound-gateway
id="outboundGateway"
url="{url2}"
http-method="GET"
request-factory="requestFactory"
charset="UTF-8"
mapped-request-headers="Accept:application/json"
expected-response-type="java.lang.String"></int-http:outbound-gateway>
<int:object-to-json-transformer/>
</int:chain>
<int:chain id="chain2" input-channel="publishsubscribechannel" output-channel="aggregatorChannel">
<int:transformer ref="fetchTransformer" method="process" />
</int:chain>
<int:chain id="request-chain" input-channel="publishsubscribechannel" output-channel="aggregatorChannel">
<int-http:outbound-gateway
id="strideOutboundGateway1"
url="url3"
http-method="GET"
request-factory="requestFactory"
charset="UTF-8"
mapped-request-headers="Accept:application/json"
expected-response-type="java.lang.String"></int-http:outbound-gateway>
<int:transformer ref="fetchTransformer" method="process1" />
</int:chain>
<int:chain id="chain3" input-channel="aggregatorChannel" output-channel="outputChannel">
<int:aggregator id="tAggregator"
ref="tDataAggregator"
method="processAggregator"
correlation-strategy-expression="headers['id']"
release-strategy="aggregatorReleaseStrategy"
expire-groups-upon-completion="true"/>
</int:chain>
But, I have specified the output channel in the aggregator.
Below is the exception and even if I add the reply-channel in headers, application is going for stackoverflow.
org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:452) ~[spring-integration-core-5.5.13.jar:5.5.13]
at org.springframework.integration.handler.AbstractMessageProducingHandler.doProduceOutput(AbstractMessageProducingHandler.java:325) ~[spring-integration-core-5.5.13.jar:5.5.13]
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:268) ~[spring-integration-core-5.5.13.jar:5.5.13]
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:232) ~[spring-integration-core-5.5.13.jar:5.5.13]
The transformer is different from other endpoints that it does not modify a Message we return from its method call. It is counted as transformer has a full control over the reply message creation. So, if there is a need to preserve some request message headers, it is recommended to use a MessageBuilder.copyHeadersIfAbsent() API. This way in request-reply scenario, the necessary replyChannel header is present in the reply message.
We probably have to mention shouldCopyRequestHeaders() behavior for the transformer in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/message-transformation.html#messaging-transformation-chapter. Feel free to raise a GH issue.

Spring Integration TCP - request/response switch request to the channels

I am doing Spring Integration with Server and Client request/response demo. The request comes with a Byte array which is to be converted to Object. Then the request comes to the input channel, I need to check which class is to be send to the service activator. Then the service activator will send it to the reply channel.
<bean id="javaSerializer"
class="org.springframework.core.serializer.DefaultSerializer"/>
<bean id="javaDeserializer"
class="org.springframework.core.serializer.DefaultDeserializer"/>
<!-- single-use="true" : A new message has to wait until the reply to the previous message has been received -->
<int-ip:tcp-connection-factory id="server"
type="server"
host="localhost"
port="10101"
single-use="true"
so-timeout="10000"
serializer="javaSerializer"
deserializer="javaDeserializer"
task-executor="severTaskExecutor"/>
<int-ip:tcp-inbound-gateway id="inGateway"
request-channel="inputChannel"
reply-channel="replyChannel"
connection-factory="server"
reply-timeout="10000"/>
<int:channel id="inputChannel"/>
<!-- How to switch the request to the different channel??? -->
<int:channel id="myChannelOne"/>
<int:channel id="myChannelTwo"/>
<int:service-activator
input-channel="myChannelOne"
output-channel="replyChannel"
ref="myServiceOne"
method="get" />
<int:service-activator
input-channel="myChannelTwo"
output-channel="replyChannel"
ref="myServiceTwo"
method="get" />
<int:channel id="replyChannel"/>
<bean id="myServiceOne" class="com.my.demo.MyServiceOne"/>
<bean id="myServiceTwo" class="com.my.demo.MyServiceTwo"/>
Use a payload type router https://docs.spring.io/spring-integration/docs/current/reference/html/message-routing.html#router-implementations-payloadtyperouter
<int:payload-type-router input-channel="routingChannel">
<int:mapping type="java.lang.String" channel="stringChannel" />
<int:mapping type="java.lang.Integer" channel="integerChannel" />
</int:payload-type-router>

Spring Integration Kafka threading config

I'm using spring-integration-kafka 1.1.0 with the following config. I don't quite understand about the streams config. When I increase this, does Spring automatically spawn more threads to handle the messages? e.g. when I have streams=2, does the correlated transformer and service-activator all run in 2 threads? I feel like missing some thread-executor configurations, but not sure how. Any hint is appreciated. Thanks.
<int:poller default="true" fixed-delay="10"/>
<int:channel id="tag.track">
</int:channel>
<int-kafka:inbound-channel-adapter id="kafkaInboundChannelAdapterForTagTrack" kafka-consumer-context-ref="consumerContextForTagTrack" auto-startup="true" channel="tag.track">
</int-kafka:inbound-channel-adapter>
<int-kafka:consumer-context id="consumerContextForTagTrack"
consumer-timeout="${kafka.consumer.timeout}" zookeeper-connect="zookeeperConnect">
<int-kafka:consumer-configurations>
<int-kafka:consumer-configuration group-id="${kafka.consumer.group.track}" max-messages="200">
<int-kafka:topic id="tag.track" streams="2" />
</int-kafka:consumer-configuration>
</int-kafka:consumer-configurations>
</int-kafka:consumer-context>
<int:channel id="tag.track.transformed">
<int:interceptors>
<int:wire-tap channel="event.logging" />
</int:interceptors>
</int:channel>
<int:transformer id="kafkaMessageTransformerForTagTrack"
ref="kafkaMessageTransformer" input-channel="tag.track" method="transform"
output-channel="tag.track.transformed" />
<int:service-activator input-channel="tag.track.transformed" ref="tagTrackMessageHandler" method="handleTagMessage">
<int:request-handler-advice-chain>
<ref bean="userTagRetryAdvice" />
</int:request-handler-advice-chain>
</int:service-activator>
Tried message-driven-channel-adapter, but can't get it work, the following config doesn't pick up any message. Also tried the org.springframework.integration.kafka.listener.KafkaTopicOffsetManager , it complains Offset management topic cannot have more than one partition. Also, in this adapter, how to configure the consumer group?
Is there any detailed example on how to use the message-driven-channel-adapter? The instruction on the project page is pretty high level.
<int:channel id="tag.track">
<int:queue capacity="100"/>
</int:channel>
<bean id="kafkaConfiguration" class="org.springframework.integration.kafka.core.ZookeeperConfiguration">
<constructor-arg ref="zookeeperConnect"/>
</bean>
<bean id="connectionFactory" class="org.springframework.integration.kafka.core.DefaultConnectionFactory">
<constructor-arg ref="kafkaConfiguration"/>
</bean>
<bean id="decoder" class="org.springframework.integration.kafka.serializer.common.StringDecoder"/>
<int-kafka:message-driven-channel-adapter
id="adapter"
channel="tag.track"
connection-factory="connectionFactory"
key-decoder="decoder"
payload-decoder="decoder"
max-fetch="100"
topics="tag.track"
auto-startup="true"
/>
The streams property has nothing to do with Spring itself; it's simply passed to Kafka when invoking ConsumerConnector.createMessageStreams() (each topic/streams entry is passed in the map argument).
Refer to the kafka documentation.
EDIT:
When using the high-level consumer, the kafka inbound channel adapter is polled, so the threads on which the downstream integration flow runs are not related to the kafka client threads; they are managed in the poller configuration.
You could consider using the message-driven channel adapter instead.

Spring Integration Threads Parked at Service Activator

I am having issues with threads getting parked at my Service Activators that leads to files hanging in the SftpGatewayChannel when the pool is depleted. I think it is related to the SA's having a void return, which is correct because they are only incrementing metrics.
I was able to work around the issue by adding a default-reply-timeout to the SftpGateway, but this is not ideal since there is retry advice and I don't want the threads to timeout if there is a connection issue. I would like a solution that returns the threads to the pool after a successful upload and call to the "Success" Service Activator.
<task:executor id="Tasker" rejection-policy="CALLER_RUNS" pool-size="${MaxThreads}" />
<int:channel id="SftpGatewayChannel">
<int:dispatcher task-executor="Tasker" />
</int:channel>
<int:service-activator id="SegmentStart" input-channel="SftpGatewayChannel" ref="SftpGateway" />
<int:gateway id="SftpGateway" default-request-channel="SftpOutboundChannel" error-channel="ErrorChannel" />
<int:channel id="SftpOutboundChannel" datatype="java.lang.String,java.io.File,byte[]" />
<int-sftp:outbound-channel-adapter id="SftpOutboundAdapter"
session-factory="SftpCachingSessionFactory" channel="SftpOutboundChannel" charset="UTF-8" >
<int-sftp:request-handler-advice-chain>
<ref bean="exponentialRetryAdvice" />
<bean id="SuccessAdvice" class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice" >
<property name="successChannel" ref="SuccessChannel"/>
<property name="onSuccessExpression" value="true"/>
</bean>
</int-sftp:request-handler-advice-chain>
</int-sftp:outbound-channel-adapter>
<int:channel id="ErrorChannel">
<int:interceptors>
<int:wire-tap channel="FailureChannel" />
</int:interceptors>
</int:channel>
<int:channel id="AttemptChannel" />
<int:channel id="SuccessChannel" />
<int:channel id="FailureChannel" />
<int:service-activator id="AttemptMetrics" input-channel="AttemptChannel"
expression="T(MetricsCounter).addAttempt()" />
<int:service-activator id="SuccessMetrics" input-channel="SuccessChannel"
expression="T(MetricsCounter).addSuccesses(inputMessage.Headers.messages.size())" />
<int:service-activator id="FailureMetrics" input-channel="FailureChannel"
expression="T(MetricsCounter).addFailures(payload.getFailedMessage().Headers.messages.size())" />
Yes, gateways expect a reply by default. Instead of using the default RequestReplyExchanger you could use a service-interface method with a void return void process(Message<?> m).
Alternatively, as you have done, simply add default-reply-timeout="0" on your gateway and the thread will return immediately without waiting for a reply (that will never come).
... but this is not ideal ...
The reply timeout clock only starts when the thread returns to the gateway, so it will have no impact on the downstream flow.

int-http:inbound-gateway same request Channel and different Response channel issue

i have two int-http:inbound-gateway with path as mentioned below.And points to same request channel but have different reply-channel.
http://localhost:8080/XYZ/ABCService/query -- i expected to call http:inbound-gateway with id ="XYZ"
http://localhost:8080/ABCService/query - i expected to call http:inbound-gateway with id ="default"
but what happing in its not consistence when i give request to
http://localhost:8080/XYZ/ABCService/query
it is calling "default" gateway other time its calling "XYZ" i.e not consistence. or not sure it may call correctly but instead gives response to different reply-channel ?
I am using DispatcherServlet.Below my spring-integration.xml
<int-http:inbound-gateway id="default"
path="/*Service/query"
request-channel="RequestChannel" reply-channel="ResponseChannel"
supported-methods="POST" reply-timeout="5000" request-payload-type="java.lang.String"
error-channel="ErrorChannel" mapped-request-headers="xyz-*, HTTP_REQUEST_HEADERS">
<int-http:header name="reply-type" expression="'DEFAULT'" />
</int-http:inbound-gateway>
<int-http:inbound-gateway id="XYZ"
path="/XYZ/*Service/query"
request-channel="RequestChannel" reply-channel="XYZExportTransformedChannel"
supported-methods="POST" reply-timeout="5000" request-payload-type="java.lang.String"
error-channel="ErrorChannel" mapped-request-headers="xyz-*, HTTP_REQUEST_HEADERS">
<int-http:header name="reply-type" expression="'ABC'" />
</int-http:inbound-gateway>
<!--All endpoints output chanlle is CommonResonseChannel -->
<int:channel id="CommonResponseChannel">
</int:channel>
<!-- final router -->
<int:header-value-router input-channel="CommonResponseChannel"
header-name="reply-type">
<int:mapping value="DEFAULT" channel="ResponseChannel" />
<int:mapping value="ABC" channel="XYZResponseChannel" />
</int:header-value-router>
<int:channel id="ResponseChannel">
</int:channel>
<int:channel id="XYZResponseChannel">
</int:channel>
<int:transformer input-channel="XYZResponseChannel"
output-channel="XYZExportTransformedChannel" id="TransformerChannel"
ref="objToCSVTransformer"></int:transformer>
<bean class="SomeTransformer"
id="objToCSVTransformer"></bean>
<int:channel id="XYZExportTransformedChannel" />
I have opened this question before not not very clear.Not sure how to update that.So opened new one.
You should not configure your reply channels that way; it may cause unpredictable results.
reply-channels are simply bridged to the message replyChannel header so it generally will work ok, but it's unpredictable because the bridge is set up when the gateway first receives a message.
Instead, simply omit the reply-channel attributes on the gateways and let the framework route the replies directly using the replyChannel header.
Instead of your router, configure a "bridge to nowhere" (a <bridge/> with input-channel= CommonResponseChannel" and no output-channel.
Or simply omit the output-channel on the last endpoints (instead of sending to CommonResponseChannel).

Resources