Enterprise Integration Zone is brought to you in partnership with:

John D'Emic is a technologist, developer and author. He is currently a Solutions Architect at MuleSoft and a co-author of both editions of Mule in Action. John is a DZone MVB and is not an employee of DZone and has posted 10 posts at DZone. You can read more from them at their website. View Full User Profile

Client-Side Mule

11.30.2012
| 5114 views |
  • submit to reddit

Mule  is typically used on the receiving end of service requests.  Mule flows, for instance, are generally initiated by external events like a JMS message being sent to a queue, a POSTed HTTP request, or the firing of a Quartz trigger.   Since Mule is usually deployed as a server this behavior should be expected.  What isn’t so obvious, however, is using Mule as a client of these services.  We’ll see in this blog post how MuleClient can be embedded in a non-Mule application to send and consume messages as a client.

Configuring MuleClient with Spring

For the purposes of this blog post I’ll assume we have a fairly simple Spring MVC application that deals with product data. We’ll start off by modifying a Spring MVC controller to use MuleClient to asynchronously dispatch a Product object to a JMS queue.  We’ll then see how we can modify that call to elicit a response from a temporary queue, taking advantage of Mule’s support for synchronous JMS.  Finally we’ll see how we can do a “local” integration and call it from the Spring MVC application using MuleClient.

Before we can do any of this, however, we need to wire-up a MuleClient instance in our Spring MVC application.  We’ll start by creating a mule-config.xml file in the Spring application’s ./src/main/resources/META-INF directory.  We’ll create a directory called “mule” and place the following mule-config.xml in it.

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:vm="http://www.mulesoft.org/schema/mule/vm"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:spring="http://www.springframework.org/schema/beans"
      xmlns:jms="http://www.mulesoft.org/schema/mule/jms"
      xmlns:mule-xml="http://www.mulesoft.org/schema/mule/xml"
      xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/3.2/mule.xsd
http://www.mulesoft.org/schema/mule/vm http://www.mulesoft.org/schema/mule/vm/3.2/mule-vm.xsd
http://www.mulesoft.org/schema/mule/jms http://www.mulesoft.org/schema/mule/jms/3.2/mule-jms.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.mulesoft.org/schema/mule/xml http://www.mulesoft.org/schema/mule/xml/3.2/mule-xml.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
">

    <spring:bean name="connectionFactory"
                 class="org.apache.activemq.ActiveMQConnectionFactory">
        <spring:property name="brokerURL" value="vm://activemq01"/>
    </spring:bean>

    <jms:activemq-connector name="jmsConnector" connectionFactory-ref="connectionFactory"/>

</mule>

This configuration configures the JMS connector we’ll reference from the Spring controller.  Now let’s wire MuleClient up in Spring.  For this case I’ll add the following lines to my applicationContext.xml file.

<bean name="muleClient" class="org.mule.module.client.MuleClient" scope="singleton">
    <constructor-arg value="META-INF/mule/mule-config.xml"/>
</bean>

We’re now able to inject the MuleClient singleton to the controller class, ProductController, like this:

@RequestMapping("/products")
@Controller
public class ProductController {

    @Autowired
    MuleClient muleClient;

    @RequestMapping(method = RequestMethod.POST)
    public void save(@RequestBody Product product) throws Exception {
      // ToDo implement code to dispatch to JMS
    }
}

Sending JMS messages

MuleClient is ready to go at this point.  Let’s modify the save() method to dispatch a JMS message containing the product data.

@RequestMapping(method = RequestMethod.POST)
    public void save(@RequestBody Product product) throws Exception {
        muleClient.dispatch("jms://products", product, null);
}

We’re calling MuleClient’s dispatch() method to asynchronously place the Product object on a JMS queue called “products”.

Its hopefully obvious how straightforward this is.  Implementing the same behavior with the native JMS libraries, or even Spring’s JmsTemplate, would take significantly more work.   This is further demonstrated if we modify the code to wait for a response on a temporary queue.   Assuming the “remote” side of this queue is a Mule JMS inbound-endpoint with a request-reply exchange-pattern then we just need to do the following.

@ResponseBody
@RequestMapping(method = RequestMethod.POST)
public Product save(@RequestBody Product product) throws Exception {
    MuleMessage response = muleClient.send("jms://products", product, null, 2000);
    if (response == null || response.getPayload() instanceof NullPayload) {
        throw new InvalidProductResponseException();
    } else {
        return (Product) response.getPayload();
    }
}

Changing “dispatch” to “send” causes MuleClient to wait for a response.  In this case it will wait for the Product to be returned on a temporary queue, presumably modified in some way (i.e., its ID field populated with a database identifier.)  If a response isn’t received in 2 seconds or a null response is returned then we throw an exception.  Otherwise the modified Product is returned to the web-browser or API consumer.

Auditing Product Data

Let’s assume that we need to perform auditing of the products data sent to the JMS queue, perhaps posting it to a remote API.  This could be somewhat convoluted to do in code, particularly in a controller, but is trivial to implement with a Mule flow, as illustrated below.

<flow name="submitAndAuditProductDefinitions">
    <vm:inbound-endpoint path="products" exchange-pattern="one-way"/>
    <all>
        <jms:outbound-endpoint queue="products" exchange-pattern="one-way"/>
        <http:outbound-endpoint host="api.acmesoft.com" port="80" path="productAudit"
                   exchange-pattern="one-way"/>
    </all>
</flow>

Assuming the above flow is added to the mule-config.xml configured at the beginning of this post, we just need to modify the MuleClient code in ProductController to dispatch to the VM queue rather then JMS as follows.

@RequestMapping(method = RequestMethod.POST)
public void save(@RequestBody Product product) throws Exception {
    muleClient.dispatch("vm://products", product, null);
}

Wrapping Up

You may have already been aware of MuleClient, particularly in the context of writing functional tests (where it is indispensable.)   This post illustrates that MuleClient has a role in the client side of your applications as well.  We saw how to use MuleClient to dispatch and synchronously consume messages off JMS queues.  We also saw how MuleClient facilitates adhoc integrations while embedded in a non-Mule application.  These techniques will hopefully let you use Mule to simplify not just your server side integrations, but your client side ones as well.

Published at DZone with permission of John D'Emic, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)