I am working on the below case study.
Create a Rest Service to accept Reference id.
Use the Reference Id to get data (CLOB) from Database.
Put the data(CLOB) in a channel(queue) for further processing.
Reply to the rest client with response data in JSON format {"status": true,"message": "RECEIVED"}
I have the Rest Service created and using the Ref Id am getting the data from database but I am unable to send the response back to the rest client after putting the message in a channel(queue).
The output received by Rest Client is : No reply received within timeout
Basically I want the request thread to be returned immediately after the data (CLOB) is pushed to the channel(queue).
Below is the configuration.
<int:channel id="responseChannel"/>
<int:channel id="initCalculation">
<int:queue/>
</int:channel>
<!-- GET -->
<int-http:inbound-gateway
request-channel="httpGetChannel"
reply-channel="responseChannel"
supported-methods="GET"
path="/init/{refId}"
payload-expression="#pathVariables.refId">
<int-http:request-mapping produces="application/json"/>
</int-http:inbound-gateway>
<int:chain input-channel="httpGetChannel" output-channel="initCalculation">
<int-jdbc:stored-proc-outbound-gateway
id="outbound-gateway-storedproc-get-forma" data-source="dataSource"
is-function="false"
stored-procedure-name="XX_EMPROC.GET_FRMA"
ignore-column-meta-data="true"
expect-single-result="true">
<int-jdbc:sql-parameter-definition name="V_REF_ID" direction="IN" />
<int-jdbc:sql-parameter-definition name="V_FRMA" direction="OUT" type="#{T(oracle.jdbc.OracleTypes).CLOB}"/>
<int-jdbc:parameter name="V_REF_ID" expression="payload" />
</int-jdbc:stored-proc-outbound-gateway>
<!- Convert to JSON Format -->
<int:service-activator ref="brInitGateway" method="getResponse"/>
</int:chain>
<int:outbound-channel-adapter channel="initCalculation" ref="brInitGateway" method="process"/>
Kindly advise on the corrections needed in the above.
Thanks
Look, there is no body who sends message as a reply to the <int-http:inbound-gateway>. You have declared responseChannel, but who is going to use it as an output-channel?
I'd suggest you to do this:
<publish-subscribe-channel id="responseChannel"/>
<int:chain input-channel="httpGetChannel" output-channel="responseChannel">
<int:bridge input-channel="responseChannel" output-channel="initCalculation"/>
So, what happens here:
The publish-subscribe-channel for the reply-channel makes an internal bridge to the replyChannel header as one of the subscribers.
You send a result from the <chain> to that channel. Therefore the <int-http:inbound-gateway> gets its reply.
Having that <int:bridge> from response to the initCalculation you have a second subscriber and, therefore, send a message to the required queue.
If you are not interested in the brInitGateway.getResponse() as a reply for the HTTP request, you should consider do not have that reply-channel="responseChannel" at all, but still use some <publish-subscribe-channel> to send to the queue and some transformer to prepare a reply, e.g.:
<transformer input-channel="prepareProcess" expression="' {"status": true,"message": "RECEIVED"}'"/>
This transformer is without output-channel because it is going to send its result into the replyChannel header, therefore to the <int-http:inbound-gateway> initiator.
Related
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.
I have the following workflow.
inbound-channel
splitter
task executor for split channels - all the threads execute the same workflow.
3.a. construct the request
3.b. service activator wrapper for a gateway message endpoint.
3.c. gateway wrapper over the http-outbound-gateway with the configuration of error-channel (to handle exceptions while invoking http-outbound-gateway)
3.d. http-outbound-gateway
aggregator
response out of spring integration workflow.
If an exception occurs in 3.d, the control goes to the service-activator configured for the gateway error channel.
I copy just the following from the failed message to the new header to the header passed to the error channel.
a. correlationId
b. sequenceNumber
c. sequenceSize
But while aggregating the splitter response, the DefaultAggregatingMessageGroupProcessor.java removes the conflicting headers and by that it removes the error-channel and reply-channel before providing the control to aggregator.
So once the aggregator completes it's operation it is unable to find the reply or error channel and it results in an exception.
I'm using spring-integration-core version 2.2.1 and I'm not sure why the reply-channel and error-channel is being removed during header aggregation.
Any input on resolving this issue will be of great help.
Thank You :)
EDIT 1:
Thank You very much Gary for helping me out with this scenario. I'm sharing my current configuration
<!-- SPLITTER -->
<int:splitter id="dentalSplitter" ref="dentalServiceSplitter"
method="getDentalServiceList" input-channel="dentalServiceSplitterChannel"
output-channel="dentalSplitterTaskChannel" />
<int:channel id="dentalSplitterTaskChannel">
<int:dispatcher task-executor="dentalTaskExecutor" />
</int:channel>
<int:chain input-channel="dentalSplitterTaskChannel" output-channel="dentalGatewayChannel">
<int:header-enricher>
<int:header name="CHAIN_START_TIME" expression="T(System).currentTimeMillis()" overwrite="true" />
<int:object-to-json-transformer content-type="application/json"/>
</int:chain>
<int:service-activator input-channel="dentalGatewayChannel" ref="dentalGatewayWrapper" output-channel="dentalReplyChannel" />
<int:gateway id="dentalGatewayWrapper" default-request-channel="dentalCostEstimateRequestChannel" error-channel="dentalErrorChannel"/>
<int-http:outbound-gateway id="dentalGateway"
url-expression="#urlBuilder.build('${service.endpoint}')"
http-method="POST" request-factory="clientHttpRequestFactory"
request-channel="dentalCostEstimateRequestChannel" extract-request-payload="true"
expected-response-type="com.dental.test.DentalResponse">
<int-http:request-handler-advice-chain>
<ref bean="logChainTimeInterceptor" />
</int-http:request-handler-advice-chain>
</int-http:outbound-gateway>
<!-- EXCEPTION -->
<int:chain input-channel="dentalErrorChannel" output-channel="dentalAggregatorChannel">
<int:transformer ref="commonErrorTransformer" method="dentalGracefulReturn"/>
</int:chain>
<!-- SUCCESS -->
<int:chain input-channel="dentalReplyChannel" output-channel="dentalAggregatorChannel">
<int:filter discard-channel="dentalErrorChannel"
expression="T(com.dental.util.InvocationOutcomeHelper).isOutcomeSuccess(payload?.metadata?.outcome?.code,payload?.metadata?.outcome?.message)" />
</int:chain>
<!-- AGGREGATION -->
<int:chain input-channel="dentalAggregatorChannel" output-channel="wsDentalServiceOutputChannel" >
<int:aggregator ref="dentalServiceAggregator" />
<int:service-activator ref="dentalResponseServiceActivator" />
</int:chain>
What I noticed was this, every split channel when passing through the gateway creates a new temporary channel for error and reply and after getting the response back from the gateway, it retains the preserved (original inbound) error and reply channel header. And as you had mentioned, after the control gets to the error transformer that flow of retaining the preserved headers gets broken and the aggregating message group processor receives three different instances of temporary channel and hence removes them.
I was planning to have a custom message group processor and modify the conflict resolution strategy for aggregating the header and came up with this config.
<bean id="channelPreservingAggregatingMessageHandler" class="org.springframework.integration.aggregator.AggregatingMessageHandler">
<constructor-arg name="processor" ref="channelPreservingMessageGroupProcessor"/>
</bean>
I'm yet to test this out though. But based on this discussion, this does not look like a viable solution.
And looks like my configuration for error handling in gateway is incorrect.
However, I'm confused on this statement of yours "Instead of forwarding the message directly, simply handle the error on your error flow and return the result normally to the gateway "wrapper"". If I remove the error channel how will I get the control back when an exception occurs? May be I'm missing to understand something here. Can you elaborate more on this please?
When asking questions about scenarios such as this, you generally need to show your configuration. However, I suspect you are forwarding the message from the error flow directly to the aggregator.
This is like doing a GOTO in code and breaks the scoping.
It won't work because the replyChannel header in the error message is for the gateway "wrapper", not the original upstream inbound gateway. When the aggregator gets conflicting headers, it has no choice but to drop the headers (you will see a DEBUG log message to that effect).
Instead of forwarding the message directly, simply handle the error on your error flow and return the result normally to the gateway "wrapper" (simply omit the error channel on the last element on the error flow).
The gateway will then fix up the reply so it is consistent with other messages (good and bad) and forward it to the aggregator.
You don't need to mess with headers in your error flow, just return the value you want to be aggregated along with the good results.
You should really update to a current release, or at least the latest in the 2.2.x line (2.2.6).
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
One module sends messages to the message broker every N second. The other module receive the messages from the broker. The messages is built in a method sendMessage of the service activator. The plan was to use inbound channel adapter (as in the answer) but for some reasons this solution does not work and I keep receiving "Received no Message during the poll, returning 'false'". What is wrong with this configuration?
<int-jms:inbound-channel-adapter id="keepAlivePoller" channel="keepAliveChannel" destination="keepAlive" connection-factory="connectionFactory">
<si:poller id="sendPoller" fixed-rate="${keepalive.sendinterval}" max-messages-per-poll="1"></si:poller>
</int-jms:inbound-channel-adapter>
<si:service-activator input-channel="keepAliveChannel" method="sendMessage" ref="keepAliveSender"/>
<bean class="com.foo.KeepAliveSender"/>
<si:channel id="keepAliveChannel"/>
That will only send a message if there is a message in the "keepAlive" queue.
You can simply use...
<int:inbound-channel-adapter id="keepAlivePoller" channel="keepAliveChannel"
expression="'foo'">
<si:poller id="sendPoller" fixed-rate="${keepalive.sendinterval}" />
</int:inbound-channel-adapter>
... and not use JMS at all.
The message will not have any replyTo. It just places the message on the queue and does expects any reply "one-way" however if the message processing fails, it needs to be rolled back so that other server could process it. So in-order to process that message I'm using publish-subscribe-channel like below
<int:publish-subscribe-channel id="SplitChannel">
</int:publish-subscribe-channel>
<int-jms:inbound-gateway request-channel="UChannel" request-destination-name="U" extract-request-payload="true" acknowledge="transacted" concurrent-consumers="5" max-messages-per-task="5"/>
<int:chain input-channel="UChannel">
<int-http:outbound-gateway
url="http://localhost/u.php?fileid={fileid}"
http-method="GET"
reply-channel="nullChannel">
<int-http:uri-variable name="fileid" expression="headers.fileid"/>
</int-http:outbound-gateway>
</int:chain>
I'm getting the error as follows
DEBUG: [May-30 00:43:28,768] jms.listener.DefaultMessageListenerContainer - Initiating transaction rollback on application exception
javax.jms.InvalidDestinationException: Cannot determine reply destination: Request message does not contain reply-to destination, and no default reply destination set.
DEBUG: [May-30 00:43:28,768] apache.activemq.ActiveMQSession - 43979-1369895783067-0:15:1 Transaction Rollback
A <gateway/> is for two-way integration; channel adapters are for one-way; use an <int-jms:message-driven-channel-adapter/> instead.