BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Routing Messages in Mule

Routing Messages in Mule

When it comes to integrating your applications, message routing is where the rubber meets the road. You’ve already identified your applications, you’ve selected Mule as your integration platform, and you know which Java classes and web services you want to use in your Mule services to process the messages. So how do you wire everything together so that the messages flow through the services correctly, ensuring that you get the output you need?

Mule provides powerful and flexible options for routing messages among the services in your Mule application. This article describes common messaging styles and several of the specific routers you can use.

Following is a description of the key concepts behind message routers:

  • Endpoints define the channel through which messages are sent and received. For example, a purchasing component may receive an order request over HTTP. Once the order has been processed by the component, a JMS message may be sent over a topic to notify an auditing system, and a response can be sent back over HTTP. Endpoints allow you to listen for JMS messages, send email, invoke web services, and more.
  • Inbound routers control how a service handles incoming messages, such as selectively consuming only those messages that meet specific criteria or grouping messages together that share a group ID before forwarding them on to a service for processing.
  • Outbound routers control how a message is dispatched after the service has processed it, such as sending it to a list of recipients or splitting up the message and sending the parts to different endpoints.
  • Asynchronous reply routers are used in request/response scenarios where a request is made, which triggers one or more further requests and the results of the requests need to be consolidated before a response is given. The classic example of this is where a request is made and tasks are executed in parallel. Each task must finish executing and the results processed before a response can be sent back.
  • Catch-all strategies are invoked if no routing path can be found for the current message. An inbound or outbound endpoint can be associated with a catch-all strategy so that any orphaned messages can be caught and routed to a common location.
  • Filters provide the logic used to invoke a particular router. Filters can be combined using the logic filters AndFilter, OrFilter, and NotFilter. Not all routers need to use filters, but all routers support them.

Selecting a Messaging Style

When wiring your Mule services together, new users sometimes get confused about when to use an outbound router and when it's sufficient to simply get a reply. Following is a description of the messaging styles you can use in Mule. To see a table of which messaging styles are supported by each transport, see Transports Feature Matrix in the Mule User Guide (login is required, but registration is free and only takes a moment).

Asynchronous

If you just want to "fire and forget" a message to a service, (no response to the caller is required), you can use the asynchronous messaging style. If synchronous is set to false on the inbound endpoint, the no response is returned to the caller.

For example:

<model name="Asynchronous_Message_Pattern">
  <service name="AsynchronousService">
    <inbound>
      <jms:inbound-endpoint queue="test.in" synchronous="false"/>
    </inbound>

    <component class="org.myorg.WidgetHandler"/>

    <outbound>
      <pass-through-router>
        <jms:outbound-endpoint queue="test.out">
      </pass-through-router>
    </outbound>
  </service>
</model>

Request-Response

In simple scenarios that require a response, a service receives a request on a synchronous inbound endpoint, processes the request, and then sends it back to the caller as a reply. For example, if a user enters a value in an HTML form, and you want to transform that value and display the results in the same page, you can simply configure a synchronous inbound endpoint on the service that does the transformation. This scenario does not use an outbound router. This is the request-response messaging style.

For example:

<model name="Request-Response_Message_Pattern">
  <service name="SynchronousService">

    <!-- Set synchronous to "true" to return a response -->
    <inbound>
      <http:inbound-endpoint host="localhost" port="8080"
               path="/mule/services" synchronous="true"/>
    </inbound>

    <!-- Specify the component that processes the request -->
    <component class="org.myorg.WidgetHandler"/>

  </service>
</model>

Synchronous

If you need to pass the message to a second service for additional processing, you would configure an outbound router on the first service to pass the message to the second service. After the second service processes the message, the first service sends it back to the caller as a reply. Note that setting the synchronous inbound endpoint on the first service means that the message is treated synchronously throughout all subsequent services, so you do not need to set the synchronous flag on the second service. This is the synchronous messaging style.

For example:

<model name="Synchronous_Message_Pattern">
  <service name="SynchronousService">
    <inbound>
      <!-- Set synchronous to "true" to specify that you want to return a 
response -->
<jms:inbound-endpoint queue="test.in" synchronous="true"/> </inbound> <component class="org.myorg.WidgetHandler"/> <outbound> <!-- When using the pass-through router, you must also set synchronous
to "true" on the outbound endpoint if you want to return a
response -->
<pass-through-router> <!-- Set the outbound endpoint --> <jms:outbound-endpoint queue="test.out" synchronous="true"/> </pass-through-router> </outbound> </service> <!-- Configure the second service, and set its inbound endpoint to the same
path as the outbound endpoint of the previous service. Note that you
do not have to set the synchronous flag, as the message has already
been set to synchronous by the first service. -->
<service> <inbound> <jms:inbound-endpoint queue="test.out"/> </inbound> <component class="org.myorg.WidgetProcesser"/> </service> </model>

Note: In previous versions of Mule, you had to set the remoteSync flag to true to configure synchronous processing in remote services. As of Mule 2.2, this flag has been removed, and you only set the synchronous flag to "true" to create a synchronous flow.

Asynchronous Request-Response

In the most complex scenario, you can enable request-response messaging and allow the back-end process to be forked to invoke other services, returning a reply asynchronously based on the results of multiple service invocations. You can set the inbound endpoint's synchronous flag to false, since the response will be handled by the asynchronous reply router, unless you also want to send a response to the caller. This is the asynchronous request-response messaging style.

In the following example, a request comes in on an HTTP endpoint, is broadcast to two endpoints using the Multicast router, and the results are sent asynchronously to a JMS endpoint.

<model name="Async_Request-Response_Message_Pattern">
  <service name="AsyncRequestResponseService">
    <inbound>
      <!-- Set synchronous to "false", since the response will be 
handled by the async reply router -->
<http:inbound-endpoint host="localhost" port="8080" path="/mule/services" synchronoussynchronous="false"/> </inbound> <component class="org.myorg.WidgetHandler"/> <!-- Configure the asynchronous reply settings. This example uses the
collection async reply router, which collects all the responses
before sending the reply. -->
<async-reply timeout="5000> <collection-async-reply-router/> <jms:inbound-endpoint queue="reply.queue"/> </async-reply> <!-- Specify the endpoints where you want to send the message for
processing, and set the reply-to address where you want the response
sent. -->
<outbound> <multicasting-router> <reply-to address="jms://reply.queue"/> <jms:outbound-endpoint queue="service1" synchronous="false"/> <jms:outbound-endpoint queue="service2" synchronous="false"/> </multicasting-router> </outbound> </service> </model>

For complete information on the messaging styles you can use, see Mule Messaging Styles in the Mule User Guide.

Now that you understand which messaging styles to use for routing in different scenarios, let’s look at several routers you can use for achieving finer control over message routing. For a complete list of message routers you can use, see Using Message Routers in the Mule User Guide.

Passing Messages to Another Endpoint

The pass-through router is designed to simply pass messages from one endpoint to another. This is useful for dispatching messages to a queue, for example.

You can also use the pass-through router to perform protocol bridging to another outbound endpoint. For example:

<service name="HttpProxyService">
  <inbound>
    <inbound-endpoint address="http://localhost:8888" synchronous="true"/>
  </inbound>

  <outbound>
    <pass-through-router>
      <outbound-endpoint
         address="http://www.webservicex.net#[header:http.request]"
         synchronous="true"/>
    </pass-through-router>
  </outbound>
</service>

When using the pass-through router, if you want to return a response, you must set synchronous to "true" on the outbound endpoint. Other routers such as the chaining router do not require that you set synchronous to true on the outbound endpoint and always return a response in synchronous scenarios. Therefore, if you are sending a message through several services, you might want to use the chaining router instead of the pass-through router so that you do not have to remember to set synchronous to true on each endpoint.

Filtering Messages

You can control which messages a service handles by using filters. The Selective Consumer Router works on inbound endpoints to control which messages that service will process. The Filtering Router works on outbound endpoints to control which messages the service sends along to the next endpoint. You can use a combination of these approaches to control the message flow.

For example, if you only want to process messages that don't have errors, you can use a selective consumer to ensure that only those with the result code "success" are processed. You can then use a Catch-all Strategy to forward all other messages to another endpoint for error handling:

<inbound>
  <selective-consumer-router>
    <mulexml:jxpath-filter expression="msg/header/resultcode = 'success'"/>
  </selective-consumer-router>

  <forwarding-catch-all-strategy>
    <jms:endpoint topic="error.topic"/>
  </forwarding-catch-all-strategy>
</inbound>

If you want the service to process all messages but then want to specify criteria to determine where the message is sent next, you can use filtering outbound routers. In the following example, messages that contain an exception are sent to the system administrator's email address, messages whose payload contains a specific string are sent to the string queue, and all other messages are picked up by the forwarding catch-all router and sent to an error queue:

<outbound>

  <filtering-router>
    <smtp:outbound-endpoint to="ross@muleumo.org"/>
      <payload-type-filter expectedTypeexpectedType="java.lang.Exception"/>
  </filtering-router>

  <filtering-router>
    <jms:outbound-endpoint to="string.queue"/>
    <and-filter>
      <payload-type-filter expectedType="java.lang.String"/>
      <regex-filter pattern="the quick brown (.*)"/>
    </and-filter>
  </filtering-router>

  <forwarding-catch-all-strategy>
    <jms:outbound-endpoint queue="error.queue"/>
  </forwarding-catch-all-strategy>

</outbound>

Similar routers are the forwarding router, which allows you to process some messages and selectively forward others, and the wiretap router, which allows you to process all messages and send them on as normal but also send a copy to another endpoint. For more information, see Inbound Routers in the Mule User's Guide.

Chaining Outbound Endpoints Together

Let's assume we have a validation service, and if the message fails validation, the message and its exception are forwarded to another service AND the message and its exception are returned to the caller. You could achieve this using the chaining router, which is a fast and lightweight configuration for sending a message to an endpoint and then sending the result of that endpoint to another endpoint. For example:

<chaining-router>
  <!-- First, send the message to this endpoint for validation -->

  <vm:outbound-endpoint path="ValidationService" synchronous="true"/>

  <!-- Then send the message to this endpoint if it contains an exception -->

  <vm:outbound-endpoint path="ValidationError" synchronous="true">
    <exception-type-filter expectedType="java.lang.Exception"/>
  </vm:outbound-endpoint>

</chaining-router>

Splitting Messages

A message splitter can be used to break down an outgoing message into parts and dispatch those parts over different endpoints configured on the router. For example, in an order-processing application, you might want to send different parts of the message to different services for processing. You could do this using one of the following routers:

List Message Splitter: accepts a list of objects that will be routed to different endpoints. For example:

<outbound>
  <list-message-splitter-router">
    <!-- Route orders to order.queue -->

    <jms:outbound-endpoint queue="order.queue">
      <payload-type-filter expectedType="com.foo.Order"/>
    </jms:outbound-endpoint>

    <!-- Route items to item.queue -->

    <jms:outbound-endpoint queue="item.queue">
      <payload-type-filter expectedType="com.foo.Item"/>
    </jms:outbound-endpoint>

  </list-message-splitter-router>
</outbound>

Expression Splitter Router: similar to the List Message Splitter but splits the message based on an expression that returns one or more message parts. For example:

<outbound>
  <expression-splitter-router
          evaluator="xpath"
          expression="/mule:mule/mule:model/mule:service"
          disableRoundRobin="true"
          failIfNoMatch="false">

    <outbound-endpoint ref="service1">
      <expression-filter
          evaluator="xpath"
          expression="/mule:service/@name = 'service splitter'"/>
    </outbound-endpoint>

    <outbound-endpoint ref="service2">
      <expression-filter
          evaluator="xpath"
          expression="/mule:service/@name = 'round robin deterministic'"/>
    </outbound-endpoint>

  </expression-splitter-router>
</outbound>

For information on configuring expressions, see Expressions Configuration Reference in the Mule User Guide.

You could also split a message into parts to improve performance. The Round Robin Message Splitter splits the message into parts and sends them to endpoints in a round-robin approach. The Message Chunking Router splits a single message into a number of fixed-length messages that will all be routed to the same endpoint.

After splitting messages, you use the Message Chunking Aggregator to aggregate the message parts back together again. The aggregator uses the correlation ID, which is set by the outbound router, to identify which parts belong to the same message.

<inbound>
  <message-chunking-aggregator-router>

    <expression-message-info-mapping
          correlationIdExpression="#[header:correlation]"/>
    <payload-type-filter expectedType="org.foo.some.Object"/>

  </message-chunking-aggregator-router>
</inbound>

Processing a Message Only Once

The Idempotent Receiver ensures that only unique messages are received by a service by checking the unique message ID of the incoming message. The ID can be generated from the message using an expression defined in the idExpression attribute. By default, the expression used is #[message:id], which means the underlying endpoint must support unique message IDs for this to work. In the following example, a unique ID is a combination of the message ID and the contents of the label field in the header, and the IDs are then written to a simple text file to keep track of which messages have already been processed:

<inbound>
  <idempotent-receiver-router idExpression="#[message:id]-#[header:label]">
    <simple-text-file-store directory="./idempotent"/>
  </idempotent-receiver-router>
</inbound>

Calling an External Service through Component Bindings

In addition to using messaging routers to control how messages flow between services, you can also have your component call out to an external service as it is processing the message.

In this approach, you bind Mule endpoints to Java interface methods. The advantage of this approach is that you can use an external service while the component is still processing the message, but you do not have to use Mule APIs or modify your component class. Instead, you configure a component binding in the configuration XML file to specify the endpoint of the external service. For example, the following binding causes the sayHello method in HelloInterface to call out to the external HelloWeb service when sayHello is called:

<component class="org.mule.examples.bindings.InvokerComponent">
    <binding interface="org.mule.examples.bindings.HelloInterface"
             method="sayHello">
        <cxf:outbound-endpoint
          address="http://myhost.com:81/services/HelloWeb?method=helloMethod"
          synchronous="true"/>
    </binding>
</component>

For more information, see Component Bindings in the Mule User Guide.

Summary

Mule provides you with many approaches for controlling how messages are exchanged in your application. This article has provided an overview of message routing in Mule, the messaging styles it supports, some of the more commonly used routers, and component bindings. For complete information on message routing, see the Mule User Guide.

Rate this Article

Adoption
Style

BT