Polyglot programming in Mule: Scripting pack now part of the distribution

motif

Sometimes when transforming complex data structures or applying business rules to your integration, you may face the need to add some custom code. We make our best effort to try to productize and solve every common use case we come across, but sometimes it’s just not enough. When that happens, you probably turn to the programming language you love the most for help. If you’re a Java guy, you can build your own custom components and/or transformers inside . If you’re into .NET, we recently released our .NET integration framework. We also have something we call the pack” which enables the use of scripting languages such as , Javascript, Python or Ruby inside your flows. For Mule 3.6, we decided to give it a big upgrade!

Give the pack some love

Before Mule 3.6, the community distribution of the Mule only included Groovy. Although you could download it separately, Jython, Javascript and JRuby only came bundled by default on the Enterprise distribution. Now we decided to make that experience better for our users by:

  • Including all 4 scripting languages by default in the community distribution
  • We upgraded Groovy and Jython to versions 2.3.7-indy and 2.7.3B3, which are the latest available at the moment of the 3.6 release
  • We switched to Rhino as our default implementation for Javascript, using version 1.7R4 which is also the latest.

Is Mule limited to only these four scripting languages? No! Any language supporting JSR-223 will be discovered an available to use.

Performance boost through invoke dynamic

In JDK7, a new byte code instruction called “invoke dynamic” was added to allow scripting languages to have a big performance improvement. Starting with version 3.6, JDK7 is now the minimum required JDK to run Mule which means that we can now take advantage of that Java feature. The Groovy and Jython versions that are now added to the distributions take advantage of this which means that you will see a speed boost in your groovy and python scripts!

Seamless integration with Mule

No matter which scripting language you’re using, Mule will automatically bind some variables into your so that you can interact with mule. Those properties are:

loga logger that can be used to write to Mule’s log file.
muleContexta reference to the MuleContext object.
eventContextA reference to the EventContext. This allows you to dispatch events programmatically from your script
messagethe current message.
originalPayloadthe payload of the current message before any transforms.
payloadthe transformed payload of the current message if a transformer is configured on the service. Otherwise this is the same value as originalPayload.
srcsame as payload, kept for backward compatibility.
servicea reference to the current service object.
idthe current event id.
resulta placeholder object where the result of the script can be written to. Usually it’s better to just return a value from the script unless the script method doesn’t have a return value.

 

An example

Let’s see an example of how to implement a flow which uses the “Greedy Coin Changer” algorithm. This is actually an example that ships with the Mule distribution. Below I’ll show a simplification of it, you can see the full example following this link.

This is the flow configuration:

<flow name="greedy">
        <http:inbound-endpoint host="localhost" port="${httpPort}" path="change-machine"
            exchange-pattern="request-response"/>
	<http:body-to-parameter-map-transformer />
	
	<set-payload value="#[payload['amount']]" />
        <transformer ref="StringToNumber" />
        <transformer ref="DollarsToCents"/>
        
        <choice>
            <when expression="payload.currency == 'USD'">
                <scripting:component doc:name="USD Currency Script">
                        <scripting:script file="greedy.groovy">
                            <property key="currency" value="USD"/>
                        </scripting:script>
                    </scripting:component>
                </processor-chain>
             </when>
             <when expression="payload.currency == 'GBP'">
                <processor-chain>
                    <scripting:component doc:name="GBP Currency Script">
                        <scripting:script file="greedy.py">
                            <property key="currency" value="GBP"/>
                        </scripting:script>
                    </scripting:component>
                </processor-chain>
             </when>
        </choice>
</flow>

In the above flow we see a flow which starts with a http inbound endpoint. It uses some transformers to transform the input data and then applies the algorithm, using Groovy if the currency is US Dollars or Python in the case of British pounds. This is the groovy implementation of the algorithm:

// Adapted from the Groovy Cookbook
// http://groovy.codehaus.org/Greedy+Coin+Changer+in+Groovy

enum USD {
    quarters(25), dimes(10), nickels(5), pennies(1)
    USD(v) { value = v }
    final value
}

enum GBP {
    two_pounds (200), pounds (100), fifty_pence(50), twenty_pence(20), ten_pence(10), five_pence(5), two_pence(2), pennies(1)
    GBP(v) { value = v }
    final value
}

def change(currency, amount) {
  currency.values().inject([]){ list, coin ->
     int count = amount / coin.value
     amount = amount % coin.value
     list += "$count $coin"
  }
}

switch (currency) {
    case "USD": return change(USD, payload).toString()
    case "GBP": return change(GBP, payload).toString()
    default: throw new AssertionError("Unsupported currency: $currency")
}
And this is the Python one:

Using transformers

The use of scripting languages is not limited to components, you can also use if for transformations:

<flow name="groovyTransformer">
        <script:transformer>
                <script:script engine="groovy">
                    return payload.toString().replaceAll("l", "x")
                </script:script>
        </script:transformer>
</flow>

As you can see, the groovy is bounded to the message payload through the ‘payload’ variable (although message.getPayload() would have done it as well). But is for simplicity reasons you want to pass your own parameters, you can do that as well!

Take aways

We’re now bundling Groovy, Javascript, Python and Ruby scripting languages on the CE distribution. You can also add any JSR-223 compliant language to your application. Happy scripting!


We'd love to hear your opinion on this post