Skip to main content

Exploring ESB Patterns with Mule

July 31, 2007

{cs.r.title}



The article gives a practical introduction into development
with "http://mule.mulesource.org/display/MULE/Home">Mule, a popular
open source messaging framework. First, we will briefly overview
Enterprise Service Bus concepts and will provide an introduction
to the Mule programming model. Next, we present an example employing the
Routing Slip pattern, as described in "http://www.enterpriseintegrationpatterns.com/">Enterprise
Integration Patterns
by Gregor Hohpe and Bobby Woolf, and
demonstrate how this example can be implemented using Mule
framework. We will walk through the relevant Mule configuration file
and the classes used by implementation. Finally, we will show how to install and run this example.

Enterprise Service Bus Overview

According to Mark Richards, authority on the subject, in his
presentation " "http://www.infoq.com/presentations/Enterprise-Service-Bus">The
Role of Enterprise Service Bus
," ESB can be viewed as a set of
components that provide the business tier with core integration
services:

  • Data routing
  • Data transformation
  • Protocol transformation
  • Service naming mapping
  • Message processing
  • Transaction management
  • Coordination of implementation services ("services
    orchestration")
  • Coordination of business processes ("processes
    choreography")
  • Security management

ESB evolved to be the next generation of enterprise integration
solutions, with message-oriented middleware (MOM) and web services
being considered the closest relatives. However, ESB has a broader
scope than MOM. For instance, ESB includes
such services as coordination of business processes, or protocol
transformation, that normally lay out of scope of MOM. Protocol
transformation is one of the key features of ESB, aimed at providing
communication between various incompatible systems without writing
(otherwise necessary) adapters. For example, ESB can provide a MQ
Series-to-WAP transformation facility for publishing mortgage rates
on mobile devices. In contrast, MOM usually supports a single
transport mechanism, and its services, therefore, significantly
rely on underlying message protocols, messaging data formats, error
handling mechanisms, and so on.

ESB also has a broader scope than web services. For instance,
web services frameworks usually lack such features as
publish-subscribe messaging models (the "http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wsn">WS-Notification
family of protocols is a fairly new development in this field) or
protocol transformation.

The comparison matrix below highlights some of the feature
differences between JMS, web services, and ESB (of course, this
table matrix is not a complete feature comparison).

Feature Comparison Matrix: ESB Versus JMS and Web Services

"Feature Comparison Matrix: ESB Versus JMS and Web Services">
ESB MOM Web Services
Transport layer JMS provider, SOAP provider, WSDL provider, Stream provider,
File provider, and more
JMS SOAP over HTTP
Mediation and rules Protocol translation, routing, and data transformation Routing and data transformation Data transformation
Deployment Maximum level of flexibility for dynamic configuration Requires recompiling client code Requires recompiling stubs

There are a number of ESB implementations currently available:
"http://mule.mulesource.org/wiki/display/MULE/Home">Mule,
ServiceMix, and
"http://www.sonicsoftware.com/solutions/service_oriented_architecture/enterprise_service_bus/index.ssp?Mid=58">
Sonic ESB
, just to name a few. In this article we will use Mule
as the implementation platform. This choice is motivated by:

  • The simplicity of installation and deployment of Mule.
  • The availability of the broadest scope of supported transports, and
    Inversion-of-Control containers.
  • The availability of active community support, as well as commercial
    support.
  • Our own experience using Mule in development projects.

Note that Mule is licensed under the "http://mule.mulesource.org/wiki/display/MULE/License">MuleSource
Public License
, which is a modification of the Mozilla Public
License Version 1.1.

The Mule Programming Model

Mule is described by its designers as a lightweight messaging
framework that manages communications between distributed client
service components, named as Universal Messaging Objects
(UMOs). A UMO is just a plain old Java object (POJO) that receives
and processes messages, and communicates with the rest of the
Mule-managed service components via inbound and outbound "messaging
endpoints"--transport configuration components that specify such
characteristics as transport protocol and communication address. In
our example, we use simple transport mechanisms such as system input/output and intra-VM communication channels. Outbound endpoints
are where Mule dispatches messages to another service component.
UMOs are attached to endpoints by means of inbound and outbound
routers. Outbound routers could include filtering facilities in
order to channel outbound messages to an appropriate endpoint.

Inbound and outbound messages could also undergo transformation
by an appropriate transformer attached to the UMO. An inbound
transformer provides message transformations from the transport
format into the client format immediately prior to UMO method
invocation, while the outbound transformer provides message
transformations from the client format into transport format and
sits in between the outbound message router and the message
endpoint.

Messages can also be intercepted in order to provide such
additional services as logging, collecting statistics, etc. Figure
1 illustrates the data flow from inbound to outbound endpoints as
presented in the "http://mule.mulesource.org/wiki/display/MULE/Programmers+Guide">Mule
Programmers Guide
.

<br "Message flow from inbound endpoint to outbound endpoint" width="720" height="540" />

Figure 1. Message flow from inbound endpoint to
outbound endpoint

Invaluable resources on Mule concepts and architecture can be
found at the Codehaus repository of open source projects that hosts
Mule. You may be interested in checking out the reference documentation and
other information in the "#resources">Resources section.

Here is a summary of Mule concepts that would be required to
understand this article's routing slip example.

UMO (Universal Messaging Object)
Event-driven client software component that receives and
processes a message
Inbound transformer
Component that provides message transformations from the
transport format into the client format immediately prior to UMO
method invocation
Outbound transformer
Component that provides message transformations from client
format into transport format and sits in between outbound message
router and message endpoint
Endpoint
As per the "http://mule.mulesource.org/wiki/display/MULE/Programmers+Guide">Mule
Programmers Guide
, this is a "configuration object that defines
how data will be received and transformed by Mule. On the endpoint,
you can configure the endpoint address, transport-specific
configuration information, transactions, and filters."
Inbound router
Component that controls message flow in the endpoint-to-UMO
direction
Outbound router
Component that controls message flow in the UMO-to-endpoint
direction; may include various kinds of filters to test whether the
message can be sent to a given endpoint
Interceptor
Provides additional services, like message logging, collecting
statistics, etc.
Connector
As per the "http://mule.mulesource.org/wiki/display/MULE/Architecture+Guide">Mule
Architecture Guide
, "the connector provides the implementation
for connecting to the external system"

Example: Routing Slip Pattern

Let's examine a classic "problem escalation" example. Let's
assume a customer experiences email-settings problems with an
internet services provider. The customer fills in a form, where he
or she specifies the product, the problem category, the problem subcategory, and finally, the "terminal" case
within the subcategory. Our customer is using ADSL, and
experiences an email server settings problem. Hence, the form
includes the list of keywords in this order: "ADSL, Email, Server
Settings." The customer submits the form, and the request is
processed by a product identification node, then by an ADSL node,
then by an email node, and finally by a server settings processing
node. This request escalation flow is illustrated in Figure 2.

<br "Request escalation using the Routing Slip pattern" width="526" height="76"/>
Figure 2. Request escalation using the Routing Slip pattern

The pattern when the message (the message header or its body)
includes the sequence of processing nodes is known as a routing
slip. Each router directs the message to the next channel
identified by the next key in the routing slip. The Enterprise
Integration Patterns site has an excellent "http://www.enterpriseintegrationpatterns.com/RoutingTable.html">Routing
Slip explanation
:

"How do we route a message consecutively through a series of
processing steps when the sequence of steps is not known at
design time and may vary for each message?

Attach a Routing Slip to each message, specifying the
sequence of processing steps. Wrap each component with a special
message router that reads the Routing Slip and routes the message
to the next component in the list.

We insert a special component into the beginning of the
process that computes the list of required steps for each message.
It then attaches the list as a Routing Slip to the message and
starts the process by routing the message to the first processing
step. After successful processing, each processing step looks at
the Routing Slip and passes the message to the next processing step
specified in the routing table."

Routing Slip Example Implementation

Mule Configuration

A configuration file defines how all Mule components fit
together. In our example, the configuration consists of the
following sections:

  • Connectors
  • Endpoints
  • Transformers
  • Interceptors
  • Models

The connector in this example specifies the Mule class
that is used for reading from the standard input stream and writing
into the standard output stream. The connector also defines the
prompt for entering an input message as follows:


    &lt;connector name="SystemStreamConnector"
    className="org.mule.providers.stream.SystemStreamConnector"&gt;
        &lt;properties&gt;
            &lt;property name="promptMessage"
            value="Format: route=address1:...addressN,acct=account,app=application"/&gt;
            &lt;property name="messageDelayTime" value="1000"/&gt;
        &lt;/properties&gt;
    &lt;/connector&gt;

Endpoints are defined within the tag
endpoint-identifiers. We use system input and output
as terminal endpoints. ProductUMO, the starting UMO in
the chain, refers to the input endpoint as
stream://System.in. Similarly,
EmailSettingsUMO, the terminal UMO in the chain,
refers to the output endpoint as stream://System.out.
"Virtual machine" endpoints are used as intermediate ones within
the JVM, as follows:

 
  &lt;endpoint-identifiers&gt;
            &lt;endpoint-identifier name="ADSL" value="vm://ADSL"/&gt;
            .....................................................
            &lt;endpoint-identifier name="EMAIL-LOGIN" value="vm://EMAIL-LOGIN"/&gt;
   &lt;/endpoint-identifiers&gt;

An inbound transformer transforms an inbound message into
a format that can be consumed by a UMO as input. In the
configuration fragment below, InPropertiesTransformer
translates a message from a String into a
java.util.Properties object. An outbound
transformer
transforms a message produced by a UMO in the opposite
direction, from Properties into a
String:


     &lt;transformers&gt;
        &lt;!-- Transforms message from key-value pairs into string --&gt;
        &lt;transformer name="OutPropertiesTransformer" className="com.objcentric.messaging.mule.transformers.OutPropertiesTransformer"
                        returnClass="java.lang.String"/&gt;
        &lt;!-- Transforms message into key-value pairs --&gt;
        &lt;transformer name="InPropertiesTransformer" className="com.objcentric.messaging.mule.transformers.InPropertiesTransformer"
                        returnClass="java.util.Properties"/&gt;
    &lt;/transformers&gt;

We are also using standard Mule interceptors for logging and tracing:


    &lt;interceptor-stack name="default"&gt;
         &lt;interceptor className="org.mule.interceptors.LoggingInterceptor"/&gt;
         &lt;interceptor className="org.mule.interceptors.TimerInterceptor"/&gt;
    &lt;/interceptor-stack&gt;

Components, described above, are glued together in the section
model, which lists the set of UMOs. The Mule UMO component
defines inbound/outbound transformers and inbound/outbound
routers. Here is the ProductUMO definition:


        &lt;mule-descriptor name="ProductUMO" 
                        implementation="com.objcentric.messaging.mule.processors.PassThroughProductProcessor"
                        outboundTransformer="OutPropertiesTransformer"&gt;
            &lt;!-- any number of endpoints can be added to an inbound router --&gt;
            &lt;inbound-router&gt;
                &lt;endpoint address="stream://System.in" transformers="InPropertiesTransformer"/&gt;
            &lt;/inbound-router&gt;
            &lt;!-- 
            The OutboundPassthroughRouter is a router that automically sends every
            message it receives --&gt;
            &lt;outbound-router&gt;
                &lt;catch-all-strategy className="org.mule.routing.LoggingCatchAllStrategy"/&gt;
                &lt;router className="org.mule.routing.outbound.FilteringOutboundRouter"&gt;
                    &lt;endpoint address="ADSL"/&gt;
                    &lt;filter pattern="*ADSL*" className="org.mule.routing.filters.WildcardFilter"/&gt;
                &lt;/router&gt;
                &lt;router className="org.mule.routing.outbound.FilteringOutboundRouter"&gt;
                    &lt;endpoint address="CABLE"/&gt;
                    &lt;filter pattern="*CABLE*" className="org.mule.routing.filters.WildcardFilter"/&gt;
                &lt;/router&gt;
             &lt;/outbound-router&gt;
            &lt;interceptor name="default"/&gt;
        &lt;/mule-descriptor&gt;

As seen in this configuration fragment, ProductUMO is
implemented by the class
PassThroughProductProcessor, which consumes input
messages translated by the inbound transformer. This transformer is
referred to by the inbound-router tag, while the outbound
transformer is referred to, alongside the implementation class
definition, within the tag mule-descriptor on the top
level. Note that both transformers can be referred from the "top"
level; see, for instance, EmailSettingsUMO in the
source code at the end of this article. Finally, the
outbound-router lists outbound routers that apply
pattern matching filters for directing a message to the appropriate
endpoint.

Custom Classes

As per Mule architecture, in order to implement request
escalation processing logic, we have to develop a set of custom
UMOs. Here is the chain of UMOs, starting with the
PassThroughProductProcessor UMO, where each UMO in the
chain corresponds to the processing node, as per Figure 1:


        PassThroughProductProcessor (Product UMO) --&gt;
                PassThroughADSLProcessor (ADSL UMO) --&gt;
                        PassThroughEmailProcessor (Email UMO) --&gt;
                                EndPointEmailProcessor (Email Settings UMO)

Each UMO validates the message--an account number in our example--and passes the valid message to the next processing stage. The
last UMO in this chain generates email server settings. We use the
method process as a UMO invocation point. So how does
Mule locate the appropriate UMO method? In our example, we use a
Mule feature to determine the methods by matching the method
argument with the type returned by an inbound transformer attached
to a UMO.

InPropertiesTransformer transforms a message into a
java.util.Properties object, the type that is used as
an argument by the process method. In fact, there are
other methods provided by Mule to determine a method being called
on UMO; for example, a UMO could comply with interface
Callable, or the method argument could be of the type
org.mule.umo.UMOEventContext or
java.util.Event. In any case, the determination is
done by the Mule class DynamicEntryPointResolver. Once
this method is called, the outbound message is transformed
accordingly from Properties into
String.

We use the standard Mule router,
FilteringOutboundRouter, in order to route messages by
applying pattern matching on the message body. For instance, if the
message body contains the keyword "ADSL", then the outbound router
attached to the product UMO will route message to the "ADSL" endpoint,
the one that serves as an input endpoint for the ADSL UMO.
Unmatched messages are captured by a catch-all-strategy
processor, which just stops further message processing and logs the
message.

The source code, configuration file, and batch file for this example
are available in the routingslip.zip file (see "#resources">Resources).

Installation and Example Execution

Mule Installation

Example Installation

Unzip the sample code attached to this article and drop it under
mule-1.3-rc2\samples.

Run Example

To execute the example, run the command
routing-slip.bat from the
mule-1.3-rc2\samples\routingslip\bin directory.

If you enter
ADSL:EMAIL:EMAIL-SERVER-SETTINGS,acct=1234 after
the prompt, you'll get the following console output:


C:\etc\mule-1.3-rc2\samples\routingslip\bin&gt;routing-slip.bat
MULE_HOME=..\..\..

**********************************************************************
* Mule - Universal Message Objects version 1.3-rc2                   *
* SymphonySoft Limited                                               *
* For help or more information go to http://www.muleumo.org          *
*                                                                    *
* Server started: Sunday, October 15, 2006 5:24:52 PM EDT        *
* JDK: 1.4.2_10 (mixed mode)                                         *
* OS: Windows 2000 - Service Pack 4 (5.0, x86)                       *
* Host: OBJECTCENTRIC (192.168.1.100)                                *
* ID: Mule_Routing_Slip_Sample                                       *
*                                                                    *
* Agents Running:                                                    *
*   Mule Admin: accepting connections on tcp://localhost:60504       *
**********************************************************************

Format: route=address1:...addressN,acct=account,app=applicationroute=ADSL:EMAIL:
EMAIL-SERVER-SETTINGS,acct=1234

DEBUG 2006-10-15 17:25:01,744 [ProductUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  No CorrelationId is set on the message, will set a new Id
DEBUG 2006-10-15 17:25:01,744 [ProductUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  Extracted correlation Id as: 9df2bbb0-5c93-11db-be39-91271a7aa7db
DEBUG 2006-10-15 17:25:01,754 [ProductUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  Setting Correlation info on Outbound router for endpoint: vm://ADSL
Id=9df2bbb0-5c93-11db-be39-91271a7aa7db
DEBUG 2006-10-15 17:25:01,764 [ProductUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  Message being sent to: vm://ADSL
DEBUG 2006-10-15 17:25:01,764 [ProductUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  org.mule.providers.DefaultMessageAdapter{id=9df2bbb0-5c93-11db-be39-91271a7aa7db,  payload=java.util.Properties, correlationId=9df2bbb0-5c93-11db-be39-91271a7aa7db,  correlationGroup=-1, correlationSeq=-1, encoding=UTF-8, exceptionPayload=null, properties={
MULE_CORRELATION_ID=9df2bbb0-5c93-11db-be39-91271a7aa7db
}}
DEBUG 2006-10-15 17:25:01,804 [ProductUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  Message payload: 
{route=ADSL:EMAIL:EMAIL-SERVER-SETTINGS,acct=1234}
DEBUG 2006-10-15 17:25:01,924 [ADSLUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  CorrelationId is already set to '9df2bbb0-5c93-11db-be39-91271a7aa7db' , not setting it again
DEBUG 2006-10-15 17:25:01,924 [ADSLUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  Message being sent to: vm://EMAIL
DEBUG 2006-10-15 17:25:01,924 [ADSLUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  org.mule.providers.DefaultMessageAdapter{id=9df2bbb0-5c93-11db-be39-91271a7aa7db,  payload=java.util.Properties, correlationId=9df2bbb0-5c93-11db-be39-91271a7aa7db,  correlationGroup=-1, correlationSeq=-1, encoding=UTF-8, exceptionPayload=null, properties={
MULE_ENDPOINT=vm://ADSL
MULE_CORRELATION_ID=9df2bbb0-5c93-11db-be39-91271a7aa7db
}}
DEBUG 2006-10-15 17:25:01,924 [ADSLUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  Message payload: 
{route=ADSL:EMAIL:EMAIL-SERVER-SETTINGS,acct=1234}
DEBUG 2006-10-15 17:25:01,934 [EmailUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  CorrelationId is already set to '9df2bbb0-5c93-11db-be39-91271a7aa7db' , not setting it again
DEBUG 2006-10-15 17:25:01,934 [EmailUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  Message being sent to: vm://EMAIL-SERVER-SETTINGS
DEBUG 2006-10-15 17:25:01,934 [EmailUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  org.mule.providers.DefaultMessageAdapter{id=9df2bbb0-5c93-11db-be39-91271a7aa7db,  payload=java.util.Properties, correlationId=9df2bbb0-5c93-11db-be39-91271a7aa7db,  correlationGroup=-1, correlationSeq=-1, encoding=UTF-8, exceptionPayload=null, properties={
MULE_ENDPOINT=vm://EMAIL
MULE_CORRELATION_ID=9df2bbb0-5c93-11db-be39-91271a7aa7db
}}
DEBUG 2006-10-15 17:25:01,934 [EmailUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  Message payload: 
{route=ADSL:EMAIL:EMAIL-SERVER-SETTINGS,acct=1234}
DEBUG 2006-10-15 17:25:01,954 [EmailSettingsUMO.2]  org.mule.routing.outbound.OutboundPassThroughRouter: CorrelationId is already set to  '9df2bbb0-5c93-11db-be39-91271a7aa7db' , not setting it again
DEBUG 2006-10-15 17:25:01,954 [EmailSettingsUMO.2]  org.mule.routing.outbound.OutboundPassThroughRouter: Message being sent to: stream://System.out
DEBUG 2006-10-15 17:25:01,954 [EmailSettingsUMO.2]  org.mule.routing.outbound.OutboundPassThroughRouter:  org.mule.providers.DefaultMessageAdapter{id=9df2bbb0-5c93-11db-be39-91271a7aa7db,  payload=java.util.Properties, correlationId=9df2bbb0-5c93-11db-be39-91271a7aa7db,  correlationGroup=-1, correlationSeq=-1, encoding=UTF-8, exceptionPayload=null, properties={
MULE_ENDPOINT=vm://EMAIL-SERVER-SETTINGS
MULE_CORRELATION_ID=9df2bbb0-5c93-11db-be39-91271a7aa7db
}}
DEBUG 2006-10-15 17:25:01,964 [EmailSettingsUMO.2]  org.mule.routing.outbound.OutboundPassThroughRouter: Message payload: 
{route=ADSL:EMAIL:EMAIL-SERVER-SETTINGS,acct=1234, SMTP=smtpauth.earthlink.net,  POP3=popd.earthlink.net}

Note that the order of UMOs in the log file exactly
corresponds to the sequence of keywords in the user input. The
following excerpt from the log file illustrates that
ProductUMO output is forwarded to the "ADSL" endpoint,
since the very first keyword is "ADSL":


DEBUG 2006-10-15 17:25:01,764 [ProductUMO.2] org.mule.routing.outbound.FilteringOutboundRouter:  Message being sent to: vm://ADSL

Also note how the message payload is changed by the last UMO in the
chain, EmailSettingsUMO, which resolves email server
settings, from

route=ADSL:EMAIL:EMAIL-SERVER-SETTINGS,acct=1234

to


route=ADSL:EMAIL:EMAIL-SERVER-SETTINGS,acct=1234, SMTP=smtpauth.earthlink.net,  POP3=popd.earthlink.net

Now if you try another input,
DIALUP:EMAIL:EMAIL-SERVER-SETTINGS,acct=1234, you'll
see:


Format: route=address1:...addressN,acct=account,app=applicationroute=DIALUP:EMAIL:EMAIL-SERVER-SETTINGS,acct=1234
DEBUG 2006-10-15 17:33:32,648 [ProductUMO.2] org.mule.routing.outbound.OutboundMessageRouter: Message did not match any routers on: ProductUMO invoking catch all strategy

Note that this input does not match any pattern, since there is no
filter defined that includes the "DIALUP" pattern, and, therefore,
the catch-all-strategy gets invoked with consequent
message flow termination.

Summary

Mule is a flexible framework for implementing message-driven,
event-driven systems, and SOA. Its pluggable architecture allows to
integrate various--even incompatible--messaging systems using a
consistent model. Mule is nicely integrated with popular
application containers, including Spring, HiveMind, PicoContainer,
and others. Mule's design allows you to implement a number of
enterprise integration patterns, with minimum coding effort, in a
fairly simple way. This article shows one possible implementation
of the Routing Slip pattern using Mule.

Resources

Acknowledgments

The work on this article was encouraged by participation in development projects for Algorithmics, Inc.

width="1" height="1" border="0" alt=" " />
Igor Dayen is president of ObjectCentric Solutions, a consulting company that provides Enterprise Integration and XML-based solutions for financial services institutions.
Related Topics >> Web Services and XML   |