How To Use JMS ActiveMQ With Mule 4 - Part 5
In this blog, we will see how to use the Mulesoft JMS connector to perform the JMS guaranteed delivery.
Join the DZone community and get the full member experience.
Join For FreeThis is the 5th Part of the JMS ActiveMQ with Mule4 Series, You can read previous parts here: Part 1, Part 2, Part 3, and Part 4. In this post, we will learn about the JMS guaranteed delivery.
Guaranteed/Persistent Delivery in JMS:
We can make sure that there is no message loss while using the JMS for message transfer with mule API. There might be a possibility that a published message is not processed successfully due to any error/issue or app crash etc. so for such cases JMS can ensure that the unprocessed message is not lost and will be moved to a Dead Letter Queue and from there we can consume the message again.
Before moving ahead, let us know what is a Dead Letter Queue.
Dead Letter Queue is used to hold messages that can't be delivered to any receiver, or messages that couldn't be processed.
Now, let's first understand with a demo that why we need a persistent/Guaranteed delivery mechanism.
We have a mule flow JMS-demo-flow1 as shown in the below image and this flow gets triggered by an HTTP Requester, then we have a logger which will display the received message in logs, next we have the JMS Publish component which will publish the message into the JMS Queue and lastly we have a logger which will display a success message that the flow is completed.
In the below-given image, we can see that we have only done JMS connector Configuration and added the Destination Queue Name “sampleQueue1”, the rest of everything is the default.
We have second flow named as jms-demo-flow2. This flow has a JMS on New Message Listener, which will pick up the newly available message from the Queue “sampleQueue1” then we have a logger which will display the received message and lastly we have a HTTP Requester. In HTTP Requester we are giving an incorrect url path purposely so that it will fail.
Now let's trigger the below message from the postman and see how our API behaves.
We can see that JMS-demo-flow1 starts as soon as we hit the mule endpoint from Postman. In the logs, we can see the message received from the postman and the correlation_ID.
We can see at the JMS console and confirm that the message is still in SampleQueue1 till now as shown in the below image:
As soon as the message gets published, the On New Message Listener component of the JMS-demo-flow2 will pick the message. We can see the same message with the same correlation ID in the below image:
On processing further, as expected, we get a failure because we have configured an incorrect URL in the HTTP requestor.
So the message sent from Postman is published successfully into SampleQueue1. But when this message is picked for processing we got an error in-between and processing is stopped. But we have lost the message. The message which we were processing is no more in SampleQueue1. Below are the snips of the JMS showing the same:
We can see there is no pending message in the queue.
Below are the Mule Logs:
INFO 2021-07-19 19:52:59,714 [[MuleRuntime].uber.03: [jms-demo].jms-demo-flow1.CPU_LITE @6c2b2dcc] [processor: jms-demo-flow1/processors/0; event: 8042abc0-e89b-11eb-9b4e-c8b29becab99] org.mule.runtime.core.internal.processor.LoggerMessageProcessor: { "Message": "This is a sample message" } INFO 2021-07-19 19:53:23,410 [[MuleRuntime].uber.03: [jms-demo].jms-demo-flow1.CPU_LITE @6c2b2dcc] [processor: jms-demo-flow1/processors/2; event: 8042abc0-e89b-11eb-9b4e-c8b29becab99] org.mule.runtime.core.internal.processor.LoggerMessageProcessor: Message Published Successfully INFO 2021-07-19 19:54:21,619 [[MuleRuntime].uber.04: [jms-demo].jms-demo-flow2.CPU_LITE @62f84f84] [processor: jms-demo-flow2/processors/0; event: 8042abc0-e89b-11eb-9b4e-c8b29becab99] org.mule.runtime.core.internal.processor.LoggerMessageProcessor: { "Message": "This is a sample message" } ERROR 2021-07-19 19:56:08,634 [[jms-demo].http.requester.requestConfig.08 SelectorRunner] [processor: jms-demo-flow2/processors/1; event: 8042abc0-e89b-11eb-9b4e-c8b29becab99] org.mule.extension.http.internal.request.HttpRequester: Error sending HTTP request to http://localhost:8081/first ERROR 2021-07-19 19:56:42,458 [[jms-demo].http.requester.requestConfig.08 SelectorRunner] [processor: jms-demo-flow2/processors/1; event: 8042abc0-e89b-11eb-9b4e-c8b29becab99] org.mule.runtime.core.internal.exception.OnErrorPropagateHandler: ******************************************************************************** Message : HTTP GET on resource 'http://localhost:8081/first' failed: Connection refused: no further information. Element : jms-demo-flow2/processors/1 @ jms-demo:jms-guranteed-delivery.xml:22 (Request) Element DSL : <http:request method="GET" doc:name="Request" doc:id="f9cf1e9d-8d11-484e-be93-c516d88494ac" url="http://localhost:8081/first"></http:request> Error type : HTTP:CONNECTIVITY FlowStack : at jms-demo-flow2(jms-demo-flow2/processors/1 @ jms-demo:jms-guranteed-delivery.xml:22 (Request)) (set debug level logging or '-Dmule.verbose.exceptions=true' for everything) |
To ensure that we don’t lose our JMS message in case of failure, we will make a small change in the configuration of publish and On New Message component of JMS. We will set Persistent to True.
Similarly, we will set Persistent delivery to true in the JMS-demo-flow2
After doing the above changes we will trigger the same message from the postman. We can see that the flow is triggered and we have a correlation ID associated with it.
Once published we can see the same message in the SampleQueue1 in JMS.
Now, as expected the message is picked by On New Message Listener and failed again.
Logs:
INFO 2021-07-19 21:55:47,966 [[MuleRuntime].uber.05: [jms-demo].jms-demo-flow1.CPU_LITE @4791f97d] [processor: jms-demo-flow1/processors/0; event: f53db3e0-e8ad-11eb-9326-c8b29becab99] org.mule.runtime.core.internal.processor.LoggerMessageProcessor: { "Message": "This is a sample message" } INFO 2021-07-19 21:57:38,340 [[MuleRuntime].uber.07: [jms-demo].jms-demo-flow2.CPU_LITE @2caa25da] [processor: jms-demo-flow2/processors/0; event: f53db3e0-e8ad-11eb-9326-c8b29becab99] org.mule.runtime.core.internal.processor.LoggerMessageProcessor: { "Message": "This is a sample message" } INFO 2021-07-19 21:57:40,665 [[MuleRuntime].uber.05: [jms-demo].jms-demo-flow1.CPU_LITE @4791f97d] [processor: jms-demo-flow1/processors/2; event: f53db3e0-e8ad-11eb-9326-c8b29becab99] org.mule.runtime.core.internal.processor.LoggerMessageProcessor: Message Published Successfully ERROR 2021-07-19 21:57:48,376 [[jms-demo].http.requester.requestConfig.08 SelectorRunner] [processor: jms-demo-flow2/processors/1; event: f53db3e0-e8ad-11eb-9326-c8b29becab99] org.mule.extension.http.internal.request.HttpRequester: Error sending HTTP request to http://localhost:8081/first ERROR 2021-07-19 21:58:16,083 [[jms-demo].http.requester.requestConfig.08 SelectorRunner] [processor: jms-demo-flow2/processors/1; event: f53db3e0-e8ad-11eb-9326-c8b29becab99] org.mule.runtime.core.internal.exception.OnErrorPropagateHandler: ******************************************************************************** Message : HTTP GET on resource 'http://localhost:8081/first' failed: Connection refused: no further information. Element : jms-demo-flow2/processors/1 @ jms-demo:jms-guranteed-delivery.xml:23 (Request) Element DSL : <http:request method="GET" doc:name="Request" doc:id="f9cf1e9d-8d11-484e-be93-c516d88494ac" url="http://localhost:8081/first"></http:request> Error type : HTTP:CONNECTIVITY FlowStack : at jms-demo-flow2(jms-demo-flow2/processors/1 @ jms-demo:jms-guranteed-delivery.xml:23 (Request)) (set debug level logging or '-Dmule.verbose.exceptions=true' for everything) ******************************************************************************** |
Let's have a look at the JMS Console, here we can see that one more queue is created with the name ActiveMQ.DLQ and it has one message in it.
We can see that the message which was failed while processing is now inside ActiveMQ.DLQ queue. We can confirm it is the same message by looking at either the message details or the Correlation_ID.
In this way, we can use persistent delivery and keep all our failed messages inside a DeadLetter Queue, and from this queue, we can again trigger it or process it as per our requirement. This ensures guaranteed delivery because no message is lost.
Please note: In the above demo, the DeadLetter queue (ActiveMQ.DLQ) is a default DLQ queue for all the queues, which means if the message is failed from any queue it would be moved to the same queue ActiveMQ.DLQ.
We can create a dedicated Dead Letter Queue for each queue we have, to distinguish which failed messages belong to which queue and for this, we need to replace the destinationPolicy tag of the activemq.xml file which is present inside the conf folder in the ActiveMQ folder location.
<destinationPolicy>
<policyMap>
<policyEntries>
<!-- Set the following policy on all queues using the '>' wildcard -->
<policyEntry queue=">">
<deadLetterStrategy>
<!-- Using the prefix 'DLQ.' for the destination name of DLQ -->
<individualDeadLetterStrategy queuePrefix="DLQ." useQueueForQueueMessages="true"/>
</deadLetterStrategy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
After making the changes in activemq.xml we need to start JMS again, and on hitting the same request from the postman, failed message is now moved to a dedicated DLQ with the name DLQ.sampleQueue1 as shown in the below image.
So now we know how to implement Guaranteed delivery in the Mulesoft for JMS.
Opinions expressed by DZone contributors are their own.
Comments