Exploring ESB Patterns with Mule Blog

Version 2

    {cs.r.title}



    The article gives a practical introduction into development withMule, 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 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 "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 WS-Notificationfamily 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

                               
    ESBMOMWeb Services
    Transport layerJMS provider, SOAP provider, WSDL provider, Stream provider, File provider, and moreJMSSOAP over HTTP
    Mediation and rulesProtocol translation, routing, and data transformationRouting and data transformationData transformation
    DeploymentMaximum level of flexibility for dynamic configurationRequires recompiling client codeRequires recompiling stubs

    There are a number of ESB implementations currently available:Mule,ServiceMix, andSonic 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 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 Mule Programmers Guide.

    Message flow from inbound endpoint to outbound endpoint
    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 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 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 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.

    Request escalation using the Routing Slip pattern
    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 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:

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

    Endpoints are defined within the tagendpoint-identifiers. We use system input and output as terminal endpoints. ProductUMO, the starting UMO in the chain, refers to the input endpoint asstream://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:

     
    <endpoint-identifiers> <endpoint-identifier name="ADSL" value="vm://ADSL"/> ..................................................... <endpoint-identifier name="EMAIL-LOGIN" value="vm://EMAIL-LOGIN"/> </endpoint-identifiers> 
    

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

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

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

     
    <interceptor-stack name="default"> <interceptor className="org.mule.interceptors.LoggingInterceptor"/> <interceptor className="org.mule.interceptors.TimerInterceptor"/> </interceptor-stack> 
    

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

     
    <mule-descriptor name="ProductUMO" implementation="com.objcentric.messaging.mule.processors.PassThroughProductProcessor" outboundTransformer="OutPropertiesTransformer"> <!-- any number of endpoints can be added to an inbound router --> <inbound-router> <endpoint address="stream://System.in" transformers="InPropertiesTransformer"/> </inbound-router> <!-- The OutboundPassthroughRouter is a router that automically sends every message it receives --> <outbound-router> <catch-all-strategy className="org.mule.routing.LoggingCatchAllStrategy"/> <router className="org.mule.routing.outbound.FilteringOutboundRouter"> <endpoint address="ADSL"/> <filter pattern="*ADSL*" className="org.mule.routing.filters.WildcardFilter"/> </router> <router className="org.mule.routing.outbound.FilteringOutboundRouter"> <endpoint address="CABLE"/> <filter pattern="*CABLE*" className="org.mule.routing.filters.WildcardFilter"/> </router> </outbound-router> <interceptor name="default"/> </mule-descriptor> 
    
    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 theinbound-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-routerlists 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 thePassThroughProductProcessor UMO, where each UMO in the chain corresponds to the processing node, as per Figure 1:

     
    PassThroughProductProcessor (Product UMO) --> PassThroughADSLProcessor (ADSL UMO) --> PassThroughEmailProcessor (Email UMO) --> 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 ajava.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 interfaceCallable, or the method argument could be of the typeorg.mule.umo.UMOEventContext orjava.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 intoString.

    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-strategyprocessor, 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).

    Installation and Example Execution

    Mule Installation

    • Download Mule from the Mule download site and unzip it into your file system (referred to here as mule-1.3-rc2).
    • Set the JAVA_HOME environment variable.
    • Optionally, set the MULE_HOME environment variable (the routing-slip command script sets the defaultMULE_HOME to mule-1.3-rc2).

    Example Installation

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

    Run Example

    To execute the example, run the commandrouting-slip.bat from themule-1.3-rc2\samples\routingslip\bin directory.

    If you enterADSL: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>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 exactlycorresponds to the sequence of keywords in the user input. The following excerpt from the log file illustrates thatProductUMO 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.

      
    http://today.java.net/im/a.gif