For a project I have to use a request-response pattern on an ActiveMQ Topic, I know it is not a good practice to answer to a topic but unfortunately I have to.
My problem is that if I use the exchange-pattern="request-response" attribute at the jms:output-endpoint (to publish the request message) the message is published twice! So the subscriber get the message twice and also send his response twice.
With the request-response Router it works (this means the message is only send once).
But I would like to use a automatically generated temporary topic, so I would rather like to use the exchange-pattern="request-response" than the request-response Router.
Is there a reason why the output-endpoint sends the message twice?
If there is no other possibility as to use the request-response Router, is it possible to generate a temporary topic there?
I made a short program with a publisher (mule.componentA) and the subscriber (mule.componentB) where the message is send twice:
<jms:activemq-connector brokerURL="tcp://localhost:61616" name="JMS_Connector" doc:name="Active MQ" specification="1.1" validateConnections="true" />
<flow doc:name="mule.componentA" name="mule.componentA">
<http:inbound-endpoint host="localhost" port="8081" doc:name="HTTP" exchange-pattern="one-way" path="componentA" />
<set-payload value="hello" doc:name="Set Payload"/>
<logger message="componentA send: parameter: #[message.payload]" level="INFO" doc:name="Logger"/>
<jms:outbound-endpoint connector-ref="JMS_Connector" doc:name="JMS" exchange-pattern="request-response" topic="topic.test"/>
<logger message="componentA received. message: #[message.payload]" level="INFO" doc:name="Logger" />
</flow>
<flow doc:name="mule.componentB" name="mule.componentB">
<jms:inbound-endpoint connector-ref="JMS_Connector" doc:name="JMS" exchange-pattern="one-way" topic="topic.test"/>
<logger message="componentB received. message: #[message.payload]" level="INFO" doc:name="Logger" />
<set-payload value="world" doc:name="Set Payload"/>
<logger message="componentB send: message: #[message.payload]" level="INFO" doc:name="Logger"/>
</flow>
The http:inbound-endpoint is the trigger to start the flow. I tried it with firefox and curl, but there were no different.
The output is:
INFO 2014-01-21 16:12:29,760 [[request_response].mule.componentA.stage1.02] org.mule.api.processor.LoggerMessageProcessor: componentA send: parameter: hello
INFO 2014-01-21 16:12:29,761 [[request_response].mule.componentA.stage1.02] org.mule.transport.service.DefaultTransportServiceDescriptor: Loading default outbound transformer: org.mule.transport.jms.transformers.ObjectToJMSMessage
INFO 2014-01-21 16:12:29,762 [[request_response].mule.componentA.stage1.02] org.mule.transport.service.DefaultTransportServiceDescriptor: Loading default response transformer: org.mule.transport.jms.transformers.ObjectToJMSMessage
WARN 2014-01-21 16:12:29,762 [[request_response].mule.componentA.stage1.02] com.mulesoft.mule.transport.jms.EeJmsMessageDispatcher: Starting patched JmsMessageReceiver
INFO 2014-01-21 16:12:29,762 [[request_response].mule.componentA.stage1.02] org.mule.lifecycle.AbstractLifecycleManager: Initialising: 'JMS_Connector.dispatcher.191522199'. Object is: EeJmsMessageDispatcher
INFO 2014-01-21 16:12:29,762 [[request_response].mule.componentA.stage1.02] org.mule.lifecycle.AbstractLifecycleManager: Starting: 'JMS_Connector.dispatcher.191522199'. Object is: EeJmsMessageDispatcher
INFO 2014-01-21 16:12:29,822 [[request_response].mule.componentB.stage1.02] org.mule.api.processor.LoggerMessageProcessor: componentB received. message: hello
INFO 2014-01-21 16:12:29,823 [[request_response].mule.componentB.stage1.02] org.mule.api.processor.LoggerMessageProcessor: componentB send: message: world
INFO 2014-01-21 16:12:29,826 [[request_response].mule.componentB.stage1.02] org.mule.transport.service.DefaultTransportServiceDescriptor: Loading default outbound transformer: org.mule.transport.jms.transformers.ObjectToJMSMessage
INFO 2014-01-21 16:12:29,828 [[request_response].mule.componentB.stage1.03] org.mule.api.processor.LoggerMessageProcessor: componentB received. message: hello
INFO 2014-01-21 16:12:29,829 [[request_response].mule.componentB.stage1.03] org.mule.api.processor.LoggerMessageProcessor: componentB send: message: world
INFO 2014-01-21 16:12:29,830 [[request_response].mule.componentB.stage1.03] org.mule.transport.service.DefaultTransportServiceDescriptor: Loading default outbound transformer: org.mule.transport.jms.transformers.ObjectToJMSMessage
INFO 2014-01-21 16:12:29,832 [[request_response].mule.componentB.stage1.02] org.mule.transport.jms.JmsReplyToHandler: Reply Message sent to: temp-topic://ID:philipps-mbp-62486-1390317145516-1:1:1 with correlationID:ID:philipps-mbp-62486-1390317145516-1:1:3:1:1
INFO 2014-01-21 16:12:29,833 [[request_response].mule.componentB.stage1.03] org.mule.transport.jms.JmsReplyToHandler: Reply Message sent to: temp-topic://ID:philipps-mbp-62486-1390317145516-1:1:1 with correlationID:ID:philipps-mbp-62486-1390317145516-1:1:3:1:2
INFO 2014-01-21 16:12:29,837 [[request_response].mule.componentA.stage1.02] org.mule.api.processor.LoggerMessageProcessor: componentA received. message: world
I would say it's probably ActiveMQ trying to ensure the message is received by subscribers. I don't quite get what you are trying to achieve here. If you are publishing something to a JMS topic, you are usually broadcasting a message to several receivers, and here you trying to do point-to-point messaging. If you can not use a JMS queue with request-response exchange-pattern instead, I would suggest you to use a topic with one-way exchange-pattern, and then add a third flow/topic to receive messages from componentB and put your response handling logic there.
Related
I have a Camel client using the JMS Component to connect with IBMMQ.
I need to
Send a JMSCorrelationID with a fixed value.
Correlate the Reply message by JMSMessageID.
My configuration is:
.setHeader("CamelJmsDestinationName", "queue:///requestQueue.AQ?targetClient=1")
.setHeader("JMSCorrelationID", "SomeValue")
.to("jms:queue:requestQueue.AQ?useMessageIDAsCorrelationID=true&replyTo=ResponseQueue")
The issue is that JMSCorrelationID is null when useMessageIDAsCorrelationID=true
JMSMessageID: ID:c3e2d840d8d4e3f14040404040404040d95c7873193bef06
JMSTimestamp: 1614870096440
JMSCorrelationID: null
When useMessageIDAsCorrelationID=false the JMSCorrelationID is set as intended, but I get a timeout on correlation. How to use both?
As a way around the issue I made the following changes to the .to() endpoint options:
exchangePattern=InOnly
includeSentJMSMessageID=true
Then the message is sent with a JMSCorrelationID, but without waiting on the reply. Thereafter I set a pollEnrich() dynamically with a simple expression using the JMSMessageID created by the broker which is filtered by a JMS Selector:
selector=JMSMessageID='${header.JMSMessageID}
The Camel route:
.setHeader("CamelJmsDestinationName", "queue:///requestQueue.AQ?targetClient=1")
.setHeader("JMSCorrelationID", "SomeValue")
.to("jms://RequestQueue?useMessageIDAsCorrelationID=false&exchangePattern=InOnly&replyTo=ReplyQueue&includeSentJMSMessageID=true")
.pollEnrich().simple("jms://ReplyQueue?selector=JMSMessageID='${header.JMSMessageID}').timeout(20000)
.log("${body}")
I'm using Camel Rest DSL to build endpoints to use as proxies between different networks.
I have created 2 endpoints. Below the code:
First:
restConfiguration().host("localhost").component("undertow").bindingMode(RestBindingMode.off);
rest("/endpoint?{1param}&{2param}")
.get("/")
.route().routeId("Ednpoint1")
.autoStartup(true)
.setProperty("uri", simple("http4://0.0.0.0:8080/endpoint?1param=${header.1param}&2param=${header.2param}"))
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.removeHeaders("CamelHttp*")
.toD("${header.uri}").endRest().responseMessage().message("${body}");
Second:
restConfiguration().host("localhost").component("undertow").bindingMode(RestBindingMode.off);
rest("/endpoint?{param1}&{param2}")
.get("/")
.route().routeId("Endpoint2")
.autoStartup(true)
.setProperty("uri", simple("http4://endpoint-destionation/service?dhi=${header.param1}&dhf=${header.param2}"))
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.removeHeaders("*")
.toD("${header.uri}").endRest().responseMessage().message("${body}");
I'm currently getting the following error:
org.apache.http.ConnectionClosedException: Premature end of Content-Length delimited message body (expected: 146541; received: 54482
at org.apache.http.impl.io.ContentLengthInputStream.read(ContentLengthInputStream.java:180)
at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:137)
at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:150)
at org.apache.camel.util.IOHelper.copy(IOHelper.java:219)
at org.apache.camel.util.IOHelper.copy(IOHelper.java:174)
at org.apache.camel.util.IOHelper.copy(IOHelper.java:170)
at org.apache.camel.component.http4.HttpProducer.doExtractResponseBodyAsStream(HttpProducer.java:414)
at org.apache.camel.component.http4.HttpProducer.extractResponseBody(HttpProducer.java:397)
at org.apache.camel.component.http4.HttpProducer.populateResponse(HttpProducer.java:242)
at org.apache.camel.component.http4.HttpProducer.process(HttpProducer.java:203)
at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
at org.apache.camel.processor.SendDynamicProcessor$1.doInAsyncProducer(SendDynamicProcessor.java:178)
at org.apache.camel.impl.ProducerCache.doInAsyncProducer(ProducerCache.java:445)
at org.apache.camel.processor.SendDynamicProcessor.process(SendDynamicProcessor.java:160)
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:548)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:138)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:101)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201)
at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:97)
at org.apache.camel.component.undertow.UndertowConsumer.handleRequest(UndertowConsumer.java:126)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:360)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1985)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1487)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1378)
at java.lang.Thread.run(Thread.java:748)
Is there a way to bypass this problem with camel? Thanks!
Found the issue.
In my case, I use Wildfly 13 to deploy my Camel Routes.
The problem is with the field Send Buffer inside HTTP Listener Undertow component.
Configuration⇒Subsystems⇒Web (Undertow)⇒Server ⇒default-server
Listener⇒HTTP Listener
I change to value to 10000000 (KBytes), and now is working fine.
I'm using Camel within a Spring boot application and integrate with RabbitMQ but am encountering strange behaviour.
My app has Restful endpointswhich convert the http request to a RabbitMQ message and publish this to a predefined exchange. There is a separate consumer app which listens to a queue and processes the messages.
I have deliberately entered an incorrect rabbitmq exchange name (invalidxchangename)to check that the application will fail if the exchange does not exist however the camel context starts without error and when I send in a first request is does not report any error. This message gets lost as there is no matching RabbitMQ exchange. When I submit a second request I receive the following exception which I would have expected on route startup.
com.rabbitmq.client.AlreadyClosedException: channel is already closed due to channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'invalidxchangename' in vhost
EDIT:
I've tried a more simple example to show the issue in Camel.
I've created a simple route as follows:
from("file:in?fileName=in.txt").log(LoggingLevel.DEBUG, "in here!").to("rabbitmq://localhost:5762/invalidexchange?declare=false");
where there is an existing RabbitMQ exchange called validexchange (so I have deliberately made a typo in the RabbitMQ uri). I would expect the camel route to fail at startup since the exchange doesn't exist, or even the first time it tries to process a new in.txt file.
What I am actually seeing in the logs is that on start up it reports no error and only on the 2nd invocation of the route does it report an error.
2015-03-11 16:17:04.356 INFO 9756 : ID-SBMELW7W-06220-59960-1426051020468-0-2 >>> (route2) from(file://in?fileName=in.txt) --> log[in here!] <<< Pattern:InOnly, Headers:...
2015-03-11 16:17:04.360 INFO 9756 : ID-SBMELW7W-06220-59960-1426051020468-0-2 >>> (route2) log[in here!] --> rabbitmq://localhost:5762/customerchannel.exchang?declare=false <<< Pattern:InOnly, Headers:...
2015-03-11 16:17:45.073 INFO 9756 : ID-SBMELW7W-06220-59960-1426051020468-0-4 >>> (route2) from(file://in?fileName=in.txt) --> log[in here!] <<< Pattern:InOnly, Headers: ...
2015-03-11 16:17:45.079 INFO 9756 : ID-SBMELW7W-06220-59960-1426051020468-0-4 >>> (route2) log[in here!] --> rabbitmq://localhost:5762/customerchannel.exchang?declare=false <<< Pattern:InOnly, Headers:...
2015-03-11 16:17:45.092 ERROR 9756 : Failed delivery for (MessageId: ID-SBMELW7W-06220-59960-1426051020468-0-3 on ExchangeId: ID-SBMELW7W-06220-59960-1426051020468-0-4). Exhausted after delivery attempt: 1 caught: com.rabbitmq.client.AlreadyClosedException: channel is already closed due to channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'customerchannel.exchang' in vhost '/', class-id=60, method-id=40)
It looks like the first request is causing an error which closes the connection and logs the reason, and when you try to use the channel the second time it's returning an AlreadyClosedException with the message that caused the channel to close in the first call.
You can test this by trying to publish the second message to a different exchange name in the same channel and checking which exchange is in the error. E.g. publish the second message to invalidxchangename2 and you should still see invalidxchangename as the exchange in the error.
To fix, you should handle the publish result when you publish and re-establish the connection if there's an error.
If you want to be sure that a message got delivered to a RabbitMQ queue, then you have to use publisher confirms: https://www.rabbitmq.com/confirms.html
That you are able to publish a message it doesn't mean that the message will reach a queue. You could go to a mailbox and leave a letter inside, but between the time you left the letter there and a postman picked up, many things could have happened, for example, the mailbox catching fire and so on.
<flow name="flow1"..>
..
<file:outbound-endpoint path="${workdir}/folder/inbox"/>
..
..
</flow>
<flow name="flow2"..>
..
<file:outbound-endpoint path="${workdir}/folder/inbox"/>
..
..
</flow>
<flow name="flow3"..>
<file:inbound-endpoint connector-ref="someConnector"
path="${workdir}/folder/inbox"
moveToDirectory="${global.workdir}/123"
transformer-refs="Transformer"
moveToPattern="#[header:originalFilename]-#[function:datestamp-yyyy-MM-dd_HH-mm-ss.SSS]" />
..
..
</flow>
=================================================
My first two flows are have the same file outbound endpoint path and my third flow's inbound endpoint path is pointing to same folder location. I get the error:
Message : Failed to route event via endpoint: DefaultOutboundEndpoint{endpointUri=file://C:/temp/mule/data/vendors/inbound/856, connector=FileConnector
{
name=inboundFileConnector
lifecycle=start
this=c4092f
numberOfConcurrentTransactedReceivers=4
createMultipleTransactedReceivers=true
connected=true
supportedProtocols=[file]
serviceOverrides=<none>
}
, name='endpoint.C.mule.data.vendors.inbound.856', mep=ONE_WAY, properties={}, transactionConfig=Transaction{factory=null, action=INDIFFERENT, timeout=0}, deleteUnacceptedMessages=false, initialState=started, responseTimeout=10000, endpointEncoding=UTF-8, disableTransportTransformer=false}. Message payload is of type: ReceiverFileInputStream
Code : MULE_ERROR--2
--------------------------------------------------------------------------------
Exception stack is:
1. Stream Closed (java.io.IOException)
java.io.FileInputStream:-2 (null)
2. Failed to route event via endpoint: DefaultOutboundEndpoint{endpointUri=file://C:/mule/data/vendors/856, connector=FileConnector
{
name=inboundFileConnector
lifecycle=start
this=c4092f
numberOfConcurrentTransactedReceivers=4
createMultipleTransactedReceivers=true
connected=true
supportedProtocols=[file]
serviceOverrides=<none>
}
, name='endpoint.C..mule.data.vendors.in.856', mep=ONE_WAY, properties={}, transactionConfig=Transaction{factory=null, action=INDIFFERENT, timeout=0}, deleteUnacceptedMessages=false, initialState=started, responseTimeout=10000, endpointEncoding=UTF-8, disableTransportTransformer=false}. Message payload is of type: ReceiverFileInputStream (org.mule.api.transport.DispatchException)
org.mule.transport.AbstractMessageDispatcher:109 (http://www.mulesoft.org/docs/site/current3/apidocs/org/mule/api/transport/DispatchException.html)
--------------------------------------------------------------------------------
Root Exception stack trace:
java.io.IOException: Stream Closed
at java.io.FileInputStream.readBytes(Native Method)
at java.io.FileInputStream.read(Unknown Source)
at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:1025)
+ 3 more (set debug level logging or '-Dmule.verbose.exceptions=true' for everything)
=================================================
Can I have two outbound endpoints in a single flow, whose path is same as as inbound endpoint of another flow path?
<flow name="flow1"..>
..
<file:outbound-endpoint path="${workdir}/folder/inbox"/>
..
..
<file:outbound-endpoint path="${workdir}/folder/inbox"/>
..
</flow>
..
..
<object-to-byte-array-transformer/>
// need to convert from an input stream to a byte array to avoid having the wire-tap close it
Adding above mentioned object-to-byte-array-transformer helped to convert input stream.
Try disabling streaming in your file connector:
<file:connector name="sampleFileConnector" streaming="false" ></file:connector>
Then in all of your file endpoints refer this connector.
<file:outbound-endpoint connector-ref="sampleFileConnector" path="${workdir}/folder/inbox"/>
My ESB flow needs to get files from a dynamic folder. This folder name changes based on month and year. Hence, I configured my inbound-endpoint as shown below but I am getting below error. I really appreciate any help on this.
Flow:
<flow name="DataMapperTestFlow" doc:name="DataMapperTestFlow">
<file:inbound-endpoint path="C:\#[new Date().format('yyyy\\MMMM')]" moveToDirectory="C:\#[new Date().format('yyyy\\MMMM')]\backup" pollingFrequency="10000" responseTimeout="10000" doc:name="File">
<file:filename-regex-filter pattern=".*.xls" caseSensitive="true"/>
</file:inbound-endpoint>
<custom-transformer class="ExcelToJava" doc:name="Java"/>
<jdbc-ee:outbound-endpoint exchange-pattern="one-way" queryKey="insertTestHeaders" connector-ref="NewDatabase" doc:name="InsertHeaders"/>
<set-payload value="#[payload.excelData.excelRows]" doc:name="Set Payload"/>
<jdbc-ee:outbound-endpoint exchange-pattern="one-way" queryKey="insertTestRows" connector-ref="NewDatabase" doc:name="InsertRows"/>
</flow>
Error:
org.mule.api.endpoint.MalformedEndpointException: The endpoint
"file:///C:/#[new Date().format('yyyy/MMMM')]" is malformed and cannot
be parsed. If this is the name of a global endpoint, check the name
is correct, that the endpoint exists, and that you are using the
correct configuration (eg the "ref" attribute). Note that names on
inbound and outbound endpoints cannot be used to send or receive
messages; use a named global endpoint instead.. Only Outbound
endpoints can be dynamic
"Only Outbound endpoints can be dynamic" quite says it all. You can have a look at the Mule Requester Module if it suits your needs, or try creating endpoints/flows programmatically with a scheduler and Java/Groovy/etc code.