Using MEL for your logging, filter, content based routes and transformation needs

One of the features of Mules ESB, that I find very useful, is the Mule Expression Language (MEL). It can be used not only for flow control and filters, in fact most of the components and transformers that come out-of-the-box with Mule ESB support that properties are specified using MEL.

Warning
This article includes instructions that apply to a very old version of Mule ESB.
Some links in this article is outdated and dead.
This article need updates to the formating

One of the features of Mules ESB, that I find very useful, is the Mule Expression Language (MEL). It can be used not only for flow control and filters, in fact most of the components and transformers that come out-of-the-box with Mule ESB support that properties are specified using MEL.

The simplest example is when you want to use the default logger component to log some part of the message such as the payload and/or variables/properties.

1
<logger message="Message payload is #[payload]. The session variable 'today' contains #[sessionVars['today']]" doc:name="Log payload and session variables." level="INFO"/>

It can also be used in filters and content based routers (Choice) to determine the course of the flow. For example to only allow messages where the payload is "Hello World!"`.

1
<expression-filter expression="#[payload =='Hello World!']" />

In addition to the “normal” comparison operators MEL also provides a two more uncommon, but useful, operators ‘strsim’ and ‘soundlike’. For more information see the documentation.

MEL also includes the data extraction functions xpath() and regex(). It can be used to extract information from the message payload or any other provided context such as a attachment or property. The example below will extract the id attribute from the order element in the XML formatted payload.

1
<set-payload value="#[xpath('/order/@id')]" doc:name="Extract the id attribute from the order element"/>

When executing MEL expression the standard Java method invocation is used. This means that you can execute methods on Java objects. In the example above the payload will be set to a DefaultAttribute. So to get the actual value of the attribute, as a String, we need to call the getValue() method.

1
<expression-filter expression="#[payload.getValue() =='123']" />

This also means that you can call any Java method. For example to log the current time in milliseconds you can use:

1
<logger message="Current millis is #[java.lang.System.currentTimeMillis()]" doc:name="Log current millis" level="INFO"/>

Since MEL always imports java.lang.System we can simplify this as System.currentTimeMillis()`.

So lets say that we have a message payload that is a byte array that contains a IP address. We want to filter based on the value without altering the payload. Specifying a filter that checks a array element by element is a bit cumbersome in the Mule configuration XML so we want to convert the byte array to a String and then us it for the comparison. We can do this by using the javax.xml.bind.DatatypeConverter provided in the standard Java API.

1
2
<logger message="Payload is #[payload] which translates into #[javax.xml.bind.DatatypeConverter.printHexBinary(payload)]" doc:name="Log byte array in a more readable form." level="INFO"/>
<expression-filter expression="#[javax.xml.bind.DatatypeConverter.printHexBinary(payload) == '7F000001']" doc:name="Filter using a String comparison"/>

The code above works but is very verbose, especially if we have more complex transformations, and as you can see it also results in redundant code.

We can reduce this by defining our own global function.

1
2
3
4
5
6
7
8
<configuration doc:name="Configuration">
    <expression-language autoResolveVariables="false">
        <import class="javax.xml.bind.DatatypeConverter" /> 
        <global-functions>
            def transformBytesToString() { DatatypeConverter.printHexBinary(payload) }
        </global-functions>
    </expression-language>
</configuration>

Then we use this function in the expression.

1
2
<logger message="Payload is #[payload] which translates into #[transformBytesToString()]" doc:name="Log byte array in a more readable form." level="INFO"/>
<expression-filter expression="#[transformBytesToString() == '7F000001']" doc:name="Filter using a String comparison"/>

Mule ESB also provides other Expression Evaluators that can be used instead of and/or in combination with Mule Expression Language. I will look closer at these in a later blog. However it is worth noting that the xpath data extraction functions should not be confused with the xpath expression evaluator. They are similar but do not work exactly the same. Consider the example below:

1
2
3
4
5
6
<!-- The xpath() function returns a org.dom4j.tree.DefaultAttribute so we need to call the getValue() method. -->
<!-- However we can then use MEL operators i.e. == for equals -->
<expression-filter expression="#[xpath('/order/@id').getValue() == '123']" />
<!-- The xpath expression evaluator will evaluate the rest of the expression as a xpath expression.  -->
<!-- So we need to use xpath operators. -->
<expression-filter expression="#[xpath:/order/@id = '123']" />

Full code from this blog can be found on github.

Resources referenced in this article.