I like the idea of clean easily read XML configuration in my Mule projects. Therefore I externalize DataWeave transformations , SQL quires and other content. In this article I will compare assets that Mule gives as to achieve externalization of such things in Mule 3 and Mule 4.

Mule 3

It is up to the component if we can load content from the resource or not. The most obvious example is DataWeave Transform Message component that allows that. We have such possibility for Database component using Template query. 

Externalize SQL script using Template Query
Externalize SQL script using Template Query
Externalize DataWeave script
Externalize DataWeave script

As a result we got clear XML configuration as depicted below

<sub-flow name="get-partner-db-subflow">
  <db:select config-ref="DB" target="#[flowVars.partner]" doc:name="get Partner">
    <db:template-query-ref name="getPartner.sql"/>
      <db:in-param name="id" value="#[flowVars.id]"/>
    </db:select>
  <dw:transform-message>
    <dw:set-payload resource="classpath:templates/dwl/transformToPartner.dwl"/>
  </dw:transform-message>
</sub-flow>

It is much more readable, just by looking at XML we may infer what the code does on the high level. If we want to get know better transformation we have path to the file, that we can examine.

Mule 4

First I was not aware of new functionality delivered with this Mule version. I had a case to rewrite application to the new runtime. Application makes a couple of database calls.

When I drop Select operation on canvas I could not find any way to externalize my SQL statement. In Anypoint Studio I had field called SQL Query Text only. Below you can find the XML snippet.

<db:select doc:name="Select records"config-ref="Database_Config">
  <db:sql>
    SELECT * FROM
      users
    WHERE age > 19
  </db:sql>
</db:select>

File placeholder syntax

Then I found out that Mule has introduced new syntax to load content from the files

${file::path-to-the-file/filename}

We have three parts. The first one, file::, implies that the engine should load the file specified after double colon. Next you write the file name and path to it. The address is relative to resources folder.

As you may remember we use similar syntax to load properties like ${http.port}. Mule will try to resolve property http.port before project startup. If property is missing the project won’t start. If property is present, placeholder ${} will be replaced with the loaded value for example 8091.

File placeholder syntax is pretty the same. Mule will look for the file just before startup and won’t start if the file is missing. After the content will be loaded it will be put in place of the placeholder ${}

Information

This functionality is not only dedicated to load DataWeave files. We may load any file that we want. However when we would like to load DataWeave we need to remember to embrace our call within expression brackets #[]. Otherwise we may get text of our transformation :).

Examples

Static SQL statement

File: src/main/resources/sql/select-records.sql

<db:select doc:name="Select records" config-ref="Database_Config">
  <db:sql>${file::sql/select-records.sql}</db:sql>
</db:select>

Brief description: SQL statement from the file select-records.sql will be put between <db:sql> tags.

Choice logic

File: src/main/resources/dwl/rule.dwl

Content: sizeOf(payload) > 15

<choice doc:name="Choice">
  <when expression="${file::dwl/rule.dwl}">
    <logger level="INFO" doc:name="Logger" />
  </when>
  <otherwise >
    <set-payload value="#[payload]" doc:name="Set Payload" doc:id="ddd0085c-1d3b-4428-bc6d-fa73598ee812" />
  </otherwise>
</choice>

Brief description: content from the file rule.dwl will be put in expression attribute.

Advice

I guess that externalizing choice logic may be useful only if the expression is really complicated and long. For other cases do not use it in order to maintain greater readability.

Mock response

File: src/test/resources/test_data/system-response.dwl

Information

Why did I put response in dwl file instead of for example json? The answer is really simple. Using DataWeave I can transform and infer metadata for next event processors.
Often I have a case that input should be a Java object, but I have its JSON representation. Therefore I use dwl file with output set to application/java but in body having JSON object.

<munit-tools:mock-when doc:name="System call" processor="http:request">
  <munit-tools:then-return >
    <munit-tools:payload value='#[${file::test_data/system-response.dwl}]'/>
  </munit-tools:then-return>
</munit-tools:mock-when>

Brief description: As you may see I enclosed file placeholder with expression brackets #[]. I used this because content of the dwl file is a DataWeave script and it needs to be evaluated at runtime. If I would omit the expression brackets my mock component would return string having transformation text.

Composition

File: src/test/resources/test_data/record-to-compare.json

<munit-tools:assert-that 
  doc:name="myRecord equals" 
  expression="#[vars.myRecord]" 
  is='#[MunitTools::equalTo(${file::test_data/record-to-compare.json})]'/>

Brief description: I could provide inline object to compare in equalTo function. However I decided to externalize all examples. We may use file placeholder even in the middle of DataWeave as in the example above.

Warning

In Anypoint Studio 7.3 using file placeholder you may have errors like that one:
Description Resource Path Location Type
Invalid input ‘{‘, expected ~ (line 1, column 2):

For the time being just ignore it, you will be able to run and build your project without any problem. That is an issue with Studio.

Summary

I think that file placeholder is really great feature that gives more flexibility than we have before in Mule 3. Now we may use this wherever we like even in the middle of DataWeave transformation. It makes XML configuration files more concise and readable. Small drawback, that I see currently, is a problem with Anypoint Studio that misinforms you.

Externalize scripts
Tagged on:         

Leave a Reply

Your email address will not be published. Required fields are marked *