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.
Related
Below is code snippets, where I am trying to handle an error.
<int:channel id="errorGateWayChannel"></int:channel>
<int:service-activator ref="errorHandler"
input-channel="errorGateWayChannel" />
...
<int:service-activator.. />
<int-http:outbound-gateway ..>
<int:channel id="routerInputChannel">
<int:dispatcher task-executor="sharedPool"/></int:channel>
<int:header-enricher input-channel="routerInputChannel" output-channel="routerInputChannel1">
<int:error-channel ref="errorGateWayChannel"/>
</int:header-enricher>
<int:recipient-list-router input-channel="routerInputChannel1"
default-output-channel="nullChannel">
<int:recipient channel="datasetInputChannel"/>
</int:recipient-list-router>
<int:service-activator id="datasetInputChannelSA" input-channel="datasetInputChannel"/> <!-- now this service activator giving me error -->
Now if "datasetInputChannelSA" service activator throw any exception I am getting WARN and that not propagate to error channel.
WARN org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel - Reply message received but the receiving thread has already received a reply:ErrorMessage [payload=org.springframework.messaging.MessageHandlingException: nested exception is java.lang.RuntimeException:
I can see similar threads where they are getting WARN but not found how to handle it.
Update:
After providing overwrite="true" in "int:error-channel" it is working and sovle my issue.
<int:error-channel ref="errorGateWayChannel"/>
to
<int:error-channel ref="errorGateWayChannel" overwrite="true"/>
Your configuration is not full enough to play with on our side, so, would be great to have some small project from you somewhere on GitHub to let us to reproduce an issue if that.
However, if you would like to handle errors from the datasetInputChannelSA and don't have any other problems, you need to consider to use a <request-handler-advice-chain> for that <service-activator> and include an ExpressionEvaluatingRequestHandlerAdvice with its trapException as true. There you can configure a failureChannel to catch service exceptions and handle them in the consumer of that channel: https://docs.spring.io/spring-integration/docs/current/reference/html/#expression-advice
It's already late to set a error-channel header in an Executor context: the failed message is not going to consult downstream headers. So, your error is propagated to the TemporaryReplyChannel header populated by the upstream gateway call (not shown in your code snippet though...).
I want to do:
Receive message from web service
Send xml message to rabbit
Validate xml to xsd --> Launch exception (validation incorrect)
Return custom error message to web service
Step 1: Receive message from web service --> RESULT OK
<ws:inbound-gateway id="ws-inbound-gateway"
request-channel="requestChannel" reply-channel="replyChannel" reply-timeout="300000"
error-channel="errorChannel" />
<int:chain input-channel="requestChannel" output-channel="inbound">
<int:service-activator ref="defaultLogger" method="logger"/>
</int:chain>
Step 2: Write a xml message in fanout exchange rabbit --> RESULT OK
<int-amqp:outbound-channel-adapter
channel="inbound" amqp-template="amqpTemplate" return-channel="outbound"
exchange-name="es.queue.test"
confirm-ack-channel="confirmAck" confirm-nack-channel="confirmNack" confirm-correlation-expression="#this" />
Step 3: Validate xml to xsd --> Result ok XsdValidationException
<int:chain input-channel="confirmAck" output-channel="outbound">
<int:service-activator ref="defaultLogger" method="logger"/>
<int-xml:validating-filter schema-type="xml-schema"
schema-location="classpath:/schema/prueba.xsd"
throw-exception-on-rejection="true" discard-channel="errorChannel" />
</int:chain>
In this step the message will be sent to errorChannel but I have the next error:
63863 [AMQP Connection 10.0.9.155:5672] ERROR o.s.a.r.s.PublisherCallbackChannelImpl - Exception delivering confirm
org.springframework.integration.MessageRejectedException: Message was rejected due to XML Validation errors; nested exception is org.springframework.integration.xml.AggregatedXmlMessageValidationException: Multiple causes:
cvc-elt.1: No se ha encontrado la declaraciĆ³n del elemento 'ns2:***'.
at org.springframework.integration.xml.selector.XmlValidatingMessageSelector.accept(XmlValidatingMessageSelector.java:134)
at org.springframework.integration.filter.MessageFilter.doHandleRequestMessage(MessageFilter.java:161)
at org.springframework.integration.handler.AbstractReplyProducingPostProcessingMessageHandler.handleRequestMessage(AbstractReplyProducingPostProcessingMessageHandler.java:46)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:109)
Step 4: The web service client never receive response
I think that it's error "Exception delivering confirm" occurs because the exception is throw in thread AMQP when try send confirm ack.
Can you help me?
Everything looks good unless you should explain to us why do you use throw-exception-on-rejection="true" and break your flow?
I even think that confirm-correlation-expression="#this" really passes a requestMessage to the confirm-ack-channel, so you can send reply back to the WS Gateway from that sub-flow.
But! Since you throw-exception-on-rejection="true" nothing is going to be sent to the outbound channel.
Also return-channel is not for reply. It is another error state, when Broker can't deliver the message to the queue because of some misconfiguration there: http://www.rabbitmq.com/blog/2011/02/10/introducing-publisher-confirms/
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.
I have QueueChannel with the message-store attribute. Its configuration
<int:channel id="channel1">
<int:queue message-store="msgStore"></int:queue
</int:channel>
Then I want to send messages from channel "channel1" using <int-http:outbound-gateway/>
It looks so:
<int-http:outbound-gateway
url="someUrl"
http-method="PUT"
request-channel="channel1">
<int-http:request-handler-advice-chain>
<int:retry-advice max-attempts="3">
<int:exponential-back-off initial="1000" multiplier="2.0" maximum="8000"/>
</int:retry-advice>
</int-http:request-handler-advice-chain>
</int-http:outbound-gateway>
My problem:
If the message couldn't be sent, I need that it be in the channel "channel1". But it always is removed from channel.
How do that it remains in the channel?
EDIT1.
<int:poller
default="true"
fixed-rate="100"
receive-timeout="100">
<int:transactional/>
</int:poller>
EDIT2
<int-jdbc:message-store
id="msgStore" data-source="jdbcSQLiteDataSource"/>
Your configuration doesn't look right - you need a poller on the gateway to poll the channel for messages.
Without a transaction, you can't "leave it in the channel" after a failure; you could put it back into the channel with an ExpressionEvaluatingRequestHandlerAdvice (and a flow on its failure channel to transform the error message back to the original), but it will be at the tail of the queue, not the head.
If you message store supports transactions, you can make the poller transactional and the failure will rollback the message into the store.
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.