Migrating the VM Transport
The VM transport was completely rewritten. It evolved away from the Mule 3 transport model into an operation-based connector. This enables many new capabilities:
The ability to consume messages from a queue on demand, unlike the old transport, which only provided a polling inbound endpoint.
In Mule 3, all transport configurations are set under a top-level element called
<vm:connector />. Each configuration could handle multiple queues, but with the limitation that all of them had to be either transient or persistent. Two separate transport configs were needed in order to combine transient and persistent.
<vm:connector name="persistentVmConnector" queueTimeout="1000"> <vm:queue-profile> <default-persistent-queue-store/> </vm:queue-profile> </vm:connector>
In Mule 4, each connector configuration defines the queues that it is going to handle in advance. Each of these queues can have their own settings, allowing for clarity and flexibility. However, each config can ONLY access the queues it has defined. This is a restriction put in place to avoid publishing to a queue in which nobody is listening, which is a common mistake.
<vm:config name="vm"> <vm:queues> <vm:queue queueName="transientQueue" queueType="TRANSIENT" /> <vm:queue queueName="persistentQueue" queueType="PERSISTENT" /> </vm:queues> </vm:config>
In Mule 3, a
<vm:inbound-endpoint> message source was used to listen on a specific queue for messages. For each element in the queue, a new message was triggered.
<flow name="persistentVM"> <vm:inbound-endpoint path="persistentQueue" exchange-pattern="request-response"> <vm:transaction action="ALWAYS_BEGIN"/> </vm:inbound-endpoint> ... </flow>
This configuration defines a transactional inbound endpoint which listens to a queue called
persistentQueue. Also, the
exchange-pattern parameter was used to determine if the flow was to send a response or not. If configured to
request-response, the endpoint responds with whatever message was obtained at the end of the flow (more on this on the Migrating an Outbound Endpoint section).
In Mule 4, the
<vm:listener> message source is used instead:
<flow name="persistentVM"> <vm:listener queueName="persistentQueue" transactionalAction="ALWAYS_BEGIN" config-ref="vm"> <vm:response> <vm:content> #[lower(payload.salute)] </vm:content> </vm:response> </vm:listener> ... </flow>
Main differences are:
The listener points to a config and can only listen to a queue defined in that config.
Now, you can use the
<vm:response>element to control the response. If not provided, the message payload that results at the end of the flow will be settings.
You no longer need to use an
exchange-patternto control whether or not a response is sent. The connector automatically knows when to send it depending on the message being generated through the
Another inbound endpoint use case was listening for messages in order. In Mule 3, this had to be configured at the connector level using the
<vm:connector name="vmConnector" numberOfConcurrentTransactedReceivers="1"/>
In Mule 4, you can now do it at the listener level:
<flow name="synchronousQueue" maxConcurrency="1"> <vm:listener queueName="synchronousQueue" numberOfConsumers="1" config-ref="vm" transactionalAction="ALWAYS_BEGIN"/> ... </flow>
By setting both, the flow’s
maxConcurrency and the listener’s
numberOfConsumers to 1, you can process the messages in order.
The Mule 3 transport uses the
<vm:outbound-endpoint> component to publish a message into a queue:
<vm:outbound-endpoint path="sendAsync" exchange-pattern="one-way"/> <vm:outbound-endpoint path="sendAndWait" exchange-pattern="request-response"/>
exchange-pattern parameters play a key role in the behavior of the example above. If the pattern is
one-way, then the message will be sent and the execution continues with the same message payload. If
request-response is used, then the execution waits for a response coming from a flow with a
<vm:inbound-endpoint> listening on the same queue, and the obtained response is then propagated to the next message processor.
Mule 4 achieves this through two different operations:
<vm:publish queueName="sendAsync" config-ref="vm"> <vm:content>#[upper(payload)]</vm:content> </vm:publish> <vm:publish-consume queueName="sendAndWait" config-ref="vm"> <vm:content>#[upper(payload)]</vm:content> </vm:publish-consume>
Both operation are configured similarly and allow you to use DataWeave to build the content of the message being sent. However, while the
<vm:publish> operation publishes the content and continues with the same message, the
<vm:publish-consume> operation will wait for the response emitted by the
<vm:listener> of the referenced queue.
To use the VM connector, simply add it to your application using the Studio palette, or add the following dependency in your
<dependency> <groupId>org.mule.connectors</groupId> <artifactId>mule-vm-connector</artifactId> <version>1.1.0</version> <!-- or newer --> <classifier>mule-plugin</classifier> </dependency>