MEL Global Functions and their use

  • August, 13 2017
  • Manik Magar
  • mule
  • mel

Mule 3.X uses Mule Expression Language (based on MVEL) as default expression language. It is possible to write global MVEL functions that are reusable across Mule application. In this post, we will see how to write global functions and their use.

MEL Global Functions

MEL global functions can be configured in your Mule Config file and they are accessible from anywhere (including dataweave) in Mule application. This makes it easy to write reusable functions for any mule application.

Below is an example of configuration -

<configuration doc:name="Configuration">
    <expression-language autoResolveVariables="true">
      <global-functions file="mel_functions.mvel">

        def getSomeNumber(a,b){
          return a+b+4;
        }
      </global-functions>

    </expression-language>
</configuration>
Default Expression language in Mule 4 is DataWeave and is probably more powerful than MVEL. You may not even need Global functions in Mule 4 or they may be defined differently than in Mule 3.X. Mule 4 related changes are not covered in this post.

Let’s look at this example in detail.

Configuration element

This is a standard mule configuration element that you can include in your config file. This element allows you to configure application threading profiles, MEL expression language along with some other elements. Here, we will focus on global-functions in expression-lagnauge element.

Quick note about autoResolveVariables: It is true by default and so in MEL you can refer to variable by just name myVar instead of flowVars.myVar.

Configuration element also has another useful child element <import class="" name=""/>. Usually, common classes like java.util.*` are auto imported in MEL context, you do not need to refer classes with package name. import element, as name suggests, allows you to import any class (for example, com.javatreets.util.MyUtil) in MEL global context, similar to java imports in class file. Optionally, you may also specify an alias name for the imported class and all function then can be invoked using alias name instead of real class name.

Gloabl Functions element

There are two ways to write and load your MVEL functions -

  • Inline definitions

  • External Resource file

Inline defintions

In above, exmaple, getSomeNumber function is defined inline. You can write any number of functions in their. These functions will override functions defined with same signature in external resource, if combined. We will see more in details below.

External Resource file

It is possible to refer an external MVEL functions file using file attribute. Path should be absolute or file should be on classpath for this to work.

If functions with exact same signature are defined in external file, then I have observed that the last one in file overrides previous definitions.

MEL Function Usage

In our example above, I have written one inline function and also referring to the external file mel_functions.mvel. This file resides in src/main/resources so will be available within application, when deployed. This is how file content looks like -

def add(a,b){
	return a+b;
}

def getSomeNumber(a,b){
	return a+b+2;
}

def prepareToLog(msg){
	return '[' + flow.name + ' : ' + flowVars.requestUUID + ']' + msg;
}

def prepareMailSubject(subject, level){
	return level + ': ' + subject;
}

Let’s look at our global functions and their use:

  • add(a,b): A simple function that takes two arguments and return sum.

<munit:test name="global-functions-demo-test-add-2-params" description="MUnit Test">
        <munit:assert-on-equals expectedValue="#[3]" actualValue="#[add(1,2)]" doc:name="Assert Equals"/>
    </munit:test>
  • getSomeNumber(a,b): This is another function that adds 2 to the addition. If you notice, we have same signature function inline, and it adds 4 to the addition. So if you we use getSomeNumber, 4 should be added as it overrides the other one.

<munit:test name="global-functions-demo-test_get_somenumber_overriden" description="MUnit Test">
    <munit:assert-not-same expectedValue="#[5]" actualValue="#[getSomeNumber(1,2)]" doc:name="Assert Not Equals"/>
    <munit:assert-on-equals expectedValue="#[7]" actualValue="#[getSomeNumber(1,2)]" doc:name="Assert Equals"/>
</munit:test>
  • prepareToLog(msg): This could be very useful function. I always use this in my <logger/> messages to prefix/suffix the log messages with some common attributes. For example, this function is prefixing flow name and a request UUID variable to the message. This can ensure consistency in the logged messages as well as easy to follow through the logs for particular flow execution if requestUUID is set at the start of the flow and remains constant through out the flow execution.

<munit:test name="global-functions-demo-test_logger" description="MUnit Test">
   <set-variable variableName="requestUUID" value="#[java.util.UUID.randomUUID().toString()]" doc:name="Variable"/>
   <logger message="#[prepareToLog('Executing test case')]" level="INFO" doc:name="Logger"/>
</munit:test>

Output -

INFO  2017-08-13 14:42:35,781 [main] org.mule.api.processor.LoggerMessageProcessor:
[global-functions-demo-test_logger : 02877028-b816-4226-84f6-0e76f4b20f40]Executing test case
  • prepareMailSubject(subject, level): Another useful function that I always call when setting the subject in <smtp:outbound-endpoint/>. This helps to generate consistent email subject formats with some prefix/suffix. If you have some environment name variable defined, you can also prefix that to know from where emails are generated.

<munit:test name="global-functions-demo-test_mail-subject" description="MUnit Test">

 <munit:assert-on-equals expectedValue="#['ERROR: Something failed']" actualValue="#[prepareMailSubject('Something failed', 'ERROR')]" doc:name="Assert Equals"/>
</munit:test>

MEL Functions from DataWeave

All global functions can be access from DataWeave too, here is a test case using our add and getSomeNumber functions in weave -

    <munit:test name="global-functions-demo-dataweave-usage" description="MUnit Test">
        <dw:transform-message doc:name="Transform Message">
            <dw:set-payload><![CDATA[%dw 1.0
%output application/java
---
{
	"add": add(2,4),
	"someNumber": getSomeNumber(1,3)
}]]></dw:set-payload>
        </dw:transform-message>
        <munit:assert-on-equals expectedValue="#[{&quot;add&quot;: 6, &quot;someNumber&quot;: 8 }]" actualValue="#[payload]" doc:name="Assert Equals"/>
    </munit:test>

Conclusion

Writing reusable code always helps. MEL Global functions can help us achieve reusability of commonly used functions for mule applications.

References and Further Reading

  1. Demo Source is available on Github here

  2. Global Configuration Doc

  3. More about MEL in docs

  4. MEL functions from DataWeave

on twitter to get updates on new posts.

Stay updated!

On this blog, I post articles about different technologies like Java, MuleSoft, and much more.

You can get updates for new Posts in your email by subscribing to JavaStreets feed here -


Lives on Java Planet, Walks on Java Streets, Read/Writes in Java, JCP member, Jakarta EE enthusiast, MuleSoft Integration Architect, MuleSoft Community Ambassador, Open Source Contributor and Supporter, also writes at Unit Testers, A Family man!