This paper is published in the DZone
https://dzone.com/articles/remote-debugging-mule-applicationThursday, December 21, 2017
Mule Application Development: Effective Caching
This paper has been published at DZone:
Mule Application Development: Effective CachingSaturday, December 16, 2017
Mule Dev Tricks: Dataweave Working With XML Data
The Use Case
In my earlier post, I have explained the tricks using xpath3() to extract data from xml data. In this post I will demonstrate a use case to extract data using Dataweave.
Here is the input data (I omitted the not-interesting xml data:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | ... < wd:email_address_data > < wd:email_address >anshul.gupta277@gmail.com</ wd:email_address > < wd:usage_data wd:public = "0" > < wd:type_data wd:primary = "1" > < wd:type_reference wd:descriptor = "Home" > < wd:id wd:type = "WID" >836cf00ef5974ac08b786079866c946f</ wd:id > < wd:id wd:type = "Communication_Usage_Type_ID" >HOME</ wd:id > </ wd:type_reference > </ wd:type_data > </ wd:usage_data > </ wd:email_address_data > < wd:email_address_data > < wd:email_address >sudeshna.nanda@tcs.com</ wd:email_address > < wd:usage_data wd:public = "1" > < wd:type_data wd:primary = "1" > < wd:type_reference wd:descriptor = "Work" > < wd:id wd:type = "WID" >1f27f250dfaa4724ab1e1617174281e4</ wd:id > < wd:id wd:type = "Communication_Usage_Type_ID" >WORK</ wd:id > </ wd:type_reference > </ wd:type_data > </ wd:usage_data > </ wd:email_address_data > ... |
The desired output is an arraylist:
1 2 3 4 5 6 7 8 9 10 11 12 | [ { email=anshul.gupta277@gmail.com, wid=836cf00ef5974ac08b786079866c946f, type=HOME }, { email=sudeshna.nanda@tcs.com, wid=1f27f250dfaa4724ab1e1617174281e4, type=WORK } ] |
The Solution
The following is the solution:1 2 3 4 5 6 7 8 9 10 | %dw 1.0 %output application/java %namespace ns0 urn:com.workday/bsvc --- payload.ns0#Get_Workers_Response.ns0#Response_Data.*ns0#Worker.ns0#Worker_Data.ns0#Personal_Data.ns0#Contact_Data.*ns0#Email_Address_Data map { email: $.ns0#Email_Address, wid: $.ns0#Usage_Data.ns0#Type_Data.ns0#Type_Reference.*ns0#ID[?($.@ns0#type == "WID")][0], type: $.ns0#Usage_Data.ns0#Type_Data.ns0#Type_Reference.*ns0#ID[?($.@ns0#type == "Communication_Usage_Type_ID")][0] } |
The Key Learning Points
- Make array with wildcard *: ns0#Contact_Data.*ns0#Email_Address_Data
- Get expect element using: ns0#ID[?($.@ns0#type == "WID")][0]
- Remember: access attribute of a xml element using @
Monday, May 29, 2017
Mule Application Development - Advanced Topics (1): Global Functions
Introduction
In my previous post, I have talked about using Data-weave functions. Now, you will see that you may use those functions in different places. Sometimes, you may need to use the same function in MEL. For these kind of situations, the global function will resolve the reuse of functions. In this post, I am going to demonstrate how to you define these functions and how to use them.
Define Global Functions
Global functions can be defined any where in mule configurations files. However, it is better to define in a specific file. Here I have a file named: common.xml with the following contents:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <!--xml version= "1.0" encoding= "UTF-8" ?--> <mule xmlns= "http://www.mulesoft.org/schema/mule/core" xmlns:doc= "http://www.mulesoft.org/schema/mule/documentation" xmlns:spring= "http://www.springframework.org/schema/beans" version= "EE-3.8.3" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd" > <configuration doc:name= "Configuration" > <expression-language autoresolvevariables= "true" > <import class= "org.mule.util.StringUtils" > <import class= "org.mule.el.datetime.DateTime" > <global-functions> def getReasonId2(reason) { return StringUtils.splitAndTrim(reason, "-" )[1]; } def normalizeDate (aDateString) { date = new DateTime(aDateString, 'MM/dd/yyyy' ); date.format( 'yyyy-MM-dd' ); return date; } def mergeArray(data) { sValue = StringUtils.join(data, ',' ); return sValue } </global-functions> </import></import></expression-language> </configuration> </mule> |
In the above code, I defined 3 functions, getReasonId2, normalizeDate, and mergeArray. These functions use class of org.mule.util.StringUtils and org.mule.el.datetime.DateTime. These are imported before the global-functions element. As you can see, we are using classes from other packages. you can develop your own java classes and imported here too. We will demonstrate that in the next blog. For now, let demonstrate how to use them. Actually, the usage is the same as we use in data-weave functions. Mule runtime will be able to figure out where it is defined.
Usages
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <!--xml version= "1.0" encoding= "UTF-8" ?--> <mule xmlns:dw= "http://www.mulesoft.org/schema/mule/ee/dw" xmlns:http= "http://www.mulesoft.org/schema/mule/http" xmlns= "http://www.mulesoft.org/schema/mule/core" xmlns:doc= "http://www.mulesoft.org/schema/mule/documentation" xmlns:spring= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd http://www.mulesoft.org/schema/mule/ee/dw http://www.mulesoft.org/schema/mule/ee/dw/current/dw.xsd" > <flow name= "global_functionsFlow" > <http:listener config-ref= "HTTP_Listener_Configuration" path= "/advanced/global-function" doc:name= "HTTP" > <dw:transform-message doc:name= "Transform Message" > <dw:set-payload><!--[CDATA[%dw 1.0 %output application/json --- payload map ((record) ---> { staff_id: record. "Buyer Reference" , Contract_End_Date: normalizeDate(record. "End Date" ) as :date, Last_Day_of_Work: normalizeDate(record. "End Date" ) as :date, termination_reason: getReasonId2(record. "Closed Reason" ) })]]></dw:set-payload> </dw:transform-message> <set-variable variablename= "Test-Reason" value= "#[getReasonId2('First - Second')]" doc:name= "Variable" > <logger message= "#[payload]" level= "INFO" doc:name= "Logger" > </logger></set-variable></http:listener></flow> </mule> |
Sample Input
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [ { "Buyer Reference" : "207157" , "Start Date" : "05/01/2017" , "End Date" : "05/01/2017" , "Closed Reason" : "Voluntary - Assignment Completed" }, { "Buyer Reference" : "207157" , "Start Date" : "05/01/2017" , "End Date" : "05/01/2017" , "Closed Reason" : "Voluntary - Assignment Completed" } ] |
Output From Sample Input
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [ { "staff_id" : "207157" , "Contract_End_Date" : "2017-05-01" , "Last_Day_of_Work" : "2017-05-01" , "termination_reason" : "Assignment Completed" }, { "staff_id" : "207157" , "Contract_End_Date" : "2017-05-01" , "Last_Day_of_Work" : "2017-05-01" , "termination_reason" : "Assignment Completed" } ] |
The above code demonstrates the usage of global functions in data-weave and MEL expression.
Further Improvement
The global-functions can be defined in a file. In this way, we separate the mule configuration files with global functions definition files. The following is the improved configuration file:1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!--xml version= "1.0" encoding= "UTF-8" ?--> <mule xmlns= "http://www.mulesoft.org/schema/mule/core" xmlns:doc= "http://www.mulesoft.org/schema/mule/documentation" xmlns:spring= "http://www.springframework.org/schema/beans" version= "EE-3.8.3" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd" > <configuration doc:name= "Configuration" > <expression-language autoresolvevariables= "true" > <import class= "org.mule.util.StringUtils" > <import class= "org.mule.el.datetime.DateTime" > <global-functions file= "my_global_functions.mvel" > </global-functions> </import></import></expression-language> </configuration> </mule> |
Put my_global_functions.mven under src/main/resources, or any places within classpath.
Tuesday, May 23, 2017
Mule Dev Tricks: Create An Array List From JSON Using DataWeave
Input
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [ { "id" : "11111" , "name" : "AAAAA" }, { "id" : "22222" , "name" : "BBBBB" }, { "id" : "33333" , "name" : "CCCCC" } ] |
Output
1 | [ "11111" , "22222" , "33333" ] |
DataWeave Script
1 2 3 4 | %dw 1.0 %output application/java --- payload map (data) -> data.id |
payload map $.id
Tuesday, May 9, 2017
Mule Dev Tricks: Transform Dates Using Datawave Functions
Introduction
Processing dates is very common practice in data transformation. Dataweave's function makes this task very easy. This short blog demonstrate the procedures to do so.Normalizing Dates
As shown in the following Dataweave code. The function normalize replace "/" and "." with "-". Note: we can use replace in a concatenation style.1 2 3 4 5 6 7 8 9 10 11 | %dw 1.0 %output application/json % function normalize(date) date replace "/" with "-" replace "." with "-" --- { dates : { date1: normalize( "26-JUL-16" ) as :date {format: "dd-MMM-yy" } as :string {format: "yyyy-MM-dd" } as :date, date2: normalize( "26/JUL/16" ) as :date {format: "dd-MMM-yy" } as :string {format: "yyyy-MM-dd" } as :date, date3: normalize( "26.JUL.16" ) as :date {format: "dd-MMM-yy" } as :date {format: "yyyy-MM-dd" } } } |
1 2 3 4 5 6 7 8 9 10 11 | %dw 1.0 %output application/json % function normalize(date) date replace "/" with "-" replace "." with "-" as :date {format: "dd-MMM-yy" } as :date {format: "yyyy-MM-dd" } --- { dates : { date1: normalize( "26-JUL-16" ), date2: normalize( "26/JUL/16" ), date3: normalize( "26.JUL.16" ) } } |
Wednesday, May 3, 2017
Mule Dev Tricks: DataWeave - Using mapObject - Part IV
Introduction
In my last post I covered the topic of removing null object and empty strings from JSON payload. That solution is only good for two level of objects. If the JSON hierarchy has multiple level, then it would not work. In that case, we need to use recursion. This post will show the solution for this purpose.Sample Input
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | [ { "first_name" : "Gary" , "middle_name" : "" , "last_name" : "Liu" , "address" : { "street" : { "number" : "" , "name" : "" }, "city" : null , "zip" : null } } ] |
Desired Output
1 2 3 4 5 6 | [ { "first_name" : "Gary" , "last_name" : "Liu" } ] |
Solution
%dw 1.0 %output application/json %function skipNulls(o) o unless o is :object otherwise o mapObject { (($$): skipNulls($)) when ($ != null and $ != "") } %function skipEmpty(o) o unless o is :object otherwise o mapObject { (($$): skipEmpty($)) when ($ != {} and $ != '') } --- payload map ((person) -> { (skipEmpty(person mapObject { ($$): skipNulls($) }) ) })
Key Learning Points
In the solution given here, I used the recursion as the following:%function skipNulls(o) o unless o is :object otherwise o mapObject { (($$): skipNulls($)) when ($ != null and $ != "") }This demonstrates that dataweave functions allow us to use the recursion concept. The code provide here is not perfect yet. There is a bug I am working on.
Tuesday, May 2, 2017
Mule Dev Tricks: DavaWeave Using mapObject - Part III
Introduction
In the message flow from different end points, many data tributes are null. In particular, if the source data are from database, where a lot of attributes can be null or empty string. In this kind of scenarios, we don't want to put all the null objects and empty strings on the wire. This post explain the procedures to remove the null object or empty string from the message payload.
Sample Input
1 2 3 4 5 6 7 8 9 10 11 12 | [ { "first_name" : "Gary" , "middle_name" : "" , "last_name" : "Liu" , "address" : { "street" : null , "city" : null , "zip" : null } } ] |
Desire Output
1 2 3 4 5 6 | [ { "first_name" : "Gary" , "last_name" : "Liu" } ] |
Solution
%dw 1.0 %output application/json %function skipNulls(o) o unless o is :object otherwise o mapObject { (($$): $) when ($ != null) } %function skipEmpty(o) o mapObject { (($$): $) when ($ != {} and $ != '') } --- payload map ((person) -> { (skipEmpty(person mapObject { ($$): skipNulls($) }) ) })
Key Learning Points
This dataweave scripts is not perfect yet. More sophisticated solution will be discussed later. What we can learn from this solution is the following:- mapObject function with conditions, which are using in the functions of skipNulls and skipEmpty.
(($$): $) when ($ != {} and $ != '')
- mapObject is just a function, you can use it in custom defined function as well.
Monday, May 1, 2017
Mule Dev Tricks: DataWeave Using mapObject - Part II
Introduction
In the last part DataWeave Using mapObject - Part I. I presented a way to add new attribute to the payload. In this post, I am going to demonstrate the dataweave code for change keys of the JSON payload.Input Data
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [ { "worker_id" : "AMAGWK00002673" , "position_id" : "35119973" , "requisition_id" : "R-32353" , "applicant_id" : "199505" , "staff_id" : "199504" }, { "worker_id" : "AMAGWK00002674" , "position_id" : "35119943" , "requisition_id" : "R-32353" , "applicant_id" : "199505" , "staff_id" : "199506" }, { "worker_id" : "AMAGWK00002675" , "position_id" : "35119975" , "requisition_id" : "R-32354" , "applicant_id" : "199507" , "staff_id" : "" } ] |
Desired Result
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | [ { "ew_id" : "AMAGWK00002673" , "position_id" : "35119973" , "requisition_id" : "R-32353" , "applicant_id" : "199505" , "staff_id" : "199504" , "type" : "re-hire" }, { "ew_id" : "AMAGWK00002674" , "position_id" : "35119943" , "requisition_id" : "R-32353" , "applicant_id" : "199505" , "staff_id" : "199506" , "type" : "re-hire" }, { "ew_id" : "AMAGWK00002675" , "position_id" : "35119975" , "requisition_id" : "R-32354" , "applicant_id" : "199507" , "staff_id" : "" , "type" : "new-hire" } ] |
Solution
1 2 3 4 5 6 7 8 9 10 | %dw 1.0 %output application/json --- payload map ((worker) ->{ (worker mapObject (value, key) -> { (ew_id : value) when key as :string == 'worker_id' , ((key) : value) when (key as :string != 'worker_id' ) }), type : "new-hire" when worker.staff_id == null or worker.staff_id == '' otherwise 're-hire' }) |
Key learning Points
From the solution I provided, the key syntax is that if the key is worker_id, we will update the key with new key value which is ew_id.1 2 | (ew_id : value) when key as :string == 'worker_id' , ((key) : value) when (key as :string != 'worker_id' ) |
Sunday, April 30, 2017
Mule Dev Tricks: DataWeave Using mapObject - Part I
Introduction
Here is a problem I am facing. An incoming JSON payload misses a piece of information, the transaction type. This piece of information can be deducted from the existing information. Actually, the really application is more more complicated. In this Mule Dev Trick, I am going to demonstrate how to add new information to the payload.
This small piece of code involves several key concept of Dataweave:
- map
- mapObject
- Condition statement
- Lamda sytax
Input Payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [ { "worker_id" : "AMAGWK00002673" , "position_id" : "35119973" , "requisition_id" : "R-32353" , "applicant_id" : "199505" , "staff_id" : "199504" }, { "worker_id" : "AMAGWK00002674" , "position_id" : "35119943" , "requisition_id" : "R-32353" , "applicant_id" : "199505" , "staff_id" : "199506" }, { "worker_id" : "AMAGWK00002675" , "position_id" : "35119975" , "requisition_id" : "R-32354" , "applicant_id" : "199507" , "staff_id" : "" } ] |
The Desired Output
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | [ { "worker_id" : "AMAGWK00002673" , "position_id" : "35119973" , "requisition_id" : "R-32353" , "applicant_id" : "199505" , "staff_id" : "199504" , "type" : "re-hire" }, { "worker_id" : "AMAGWK00002674" , "position_id" : "35119943" , "requisition_id" : "R-32353" , "applicant_id" : "199505" , "staff_id" : "199506" , "type" : "re-hire" }, { "worker_id" : "AMAGWK00002675" , "position_id" : "35119975" , "requisition_id" : "R-32354" , "applicant_id" : "199507" , "staff_id" : "" , "type" : "new-hire" } ] |
Solution
1 2 3 4 5 6 7 8 9 | %dw 1.0 %output application/json --- payload map ((worker) ->{ (worker mapObject (value, key) -> { "$key" : value }), type : "new-hire" when worker.staff_id == null or worker.staff_id == '' otherwise 're-hire' }) |
1 2 3 4 5 6 7 8 9 | %dw 1.0 %output application/json --- payload map ((worker) ->{ (worker mapObject (value, key) -> { (key) : value }), type : "new-hire" when worker.staff_id == null or worker.staff_id == '' otherwise 're-hire' }) |
Key Learning Points
The above script seems pretty simple. It has used many key concepts of the DataWeave component. The key of the map function is to iterate a List or Array and produce a new array. And the main usage of mapObject is to iterate the attributes of an object. Both functions use Lamda expression which takes two value, $$, $. By default, $ represent the value of the current object the iterator is point to, and $$ is the index or key of the object.
Another worth noting point is that map can take single argument as well.
Mule Dev Tricks: DataWeave Using Reduction
Introduction
In this post, I am going to demonstrate how to convert an array of JSON payload to a single JSON payload.Input
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [ { "worker_id" : "AMAGWK00002673" , "position_id" : "35119973" , "requisition_id" : "R-32353" , "applicant_id" : "199505" , "staff_id" : "199504" }, { "worker_id" : "AMAGWK00002674" , "position_id" : "35119943" , "requisition_id" : "R-32353" , "applicant_id" : "199505" , "staff_id" : "199506" }, { "worker_id" : "AMAGWK00002675" , "position_id" : "35119975" , "requisition_id" : "R-32354" , "applicant_id" : "199507" , "staff_id" : "199508" } ] |
Desired Output
1 2 3 4 5 | { "AMAGWK00002673" : "199504" , "AMAGWK00002674" : "199506" , "AMAGWK00002675" : "199508" } |
Solution
The first step is to use dataweave to extrace the worker_id. This is very straight forward. The only trick about this piece of script is to use value as key in the transformed JSON result. This is achieved by using ($.key) : $.otherKey. here key = worker_id and the otherKey is staff_id.
1 2 3 | payload map ((value, index) -> { (value.worker_id): value.staff_id }) |
1 2 3 4 5 6 7 8 9 10 11 | [ { "AMAGWK00002673" : "199504" }, { "AMAGWK00002674" : "199506" }, { "AMAGWK00002675" : "199508" } ] |
In order to produce the final result, we need to use reduction reduce($$ + $).
1 2 3 | payload map ((value, index) -> { (value.worker_id): value.staff_id }) reduce($$ ++ $) |
Note: You have to use reduce ($$ ++$). The round bracket is important! The MuleSoft documentation missed that which causes many confusions.
Wednesday, April 19, 2017
Mule Dev Tricks: Using xpath3
Introduction
Even though today most web services using RESTful architecture, there are still a lot of legacy systems using SOAP. Handling xml file is challenging, if not difficult. Fortunately, Mule Application platform provide several utilities to alleviate the pain. The most powerful component is Dataweave, which is very handle to build SOAP request and handle responses. However, some cases, xpath3 is an easier solution. Sometimes it may be the best solution, for instance, if we want to split the xml payload based on certain node.
In this short article, I will introduce two use cases:
- Extract values from xml payload
- Split the payload
Use Case One: Extract Value Of An Element
Input Example
Here is the input:
1 2 3 4 5 6 7 8 9 10 | <!--xml version="1.0" encoding="UTF-8"?--> < wd:create_position_response xmlns:env = "http://schemas.xmlsoap.org/soap/envelope/" xmlns:wd = "urn:com.workday/bsvc" wd:version = "v27.0" > < wd:event_reference wd:descriptor = "Create Position: SOA Architect" > < wd:id wd:type = "WID" >31bf3ce3d6bb1025d71346c6ec276bd6</ wd:id > </ wd:event_reference > < wd:position_reference wd:descriptor = "SOA Architect (Unfilled)" > < wd:id wd:type = "WID" >31bf3ce3d6bb1025d71348e2f1c26bdc</ wd:id > < wd:id wd:type = "Position_ID" >35119960</ wd:id > </ wd:position_reference > </ wd:create_position_response > |
I want to extract the value of Position_ID in the line of:
1 | < wd:id wd:type = "Position_ID" >35119960</ wd:id > |
Solution
There are two solutions in this case. The first is:
1 | #[xpath3('/*:Create_Position_Response/*:Position_Reference/*:ID[2]')] |
The above MEL will assign position_id to 35119960. Note: XML node count starting from 1 not from 0 as in Java array.
The other better solution is the following:
1 | #[xpath3('/*:Create_Position_Response/*:Position_Reference/*:ID[@*:type=\'Position_ID\']')] |
- Refer element attribute using [@*:type]
- Escape the single quote with backslash \'
Use Case Two: Use xpath3 To Split Payload
Example Input
In this use case, I would like to extract the node of StockMovementData, so that I can process the data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <!--xml version="1.0"?--> < stockmovementdatarequest xmlns:p1 = "urn:ams.com.au:dynamo:3pl:am:SAP_AM_I_005:StockMovement" xmlns:a = "http://www.edi.com.au/EnterpriseService" > < header > < from >Warehouse</ from > < to >Location</ to > < unique_id >idm1468201212</ unique_id > < datetimestamp >2016-04-13T11:55:30.263+10:00</ datetimestamp > </ header > < stockmovementdata > < serialised_material >YES</ serialised_material > < datetime >2016-04-13T11:55:30.263+10:00</ datetime > < from_location >30-80</ from_location > < to_location >client</ to_location > < material >7CAGL3G00</ material > < serial >700030011</ serial > < quantity >100</ quantity > </ stockmovementdata > < stockmovementdata > < serialised_material >YES</ serialised_material > < datetime >2016-04-13T11:55:30.263+10:00</ datetime > < from_location >30-80</ from_location > < to_location >client</ to_location > < material >7CAGL3G00</ material > < serial >700029911</ serial > < quantity >100</ quantity > </ stockmovementdata > </ stockmovementdatarequest > |
Solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | < flow name = "use_xpath3_splitter-Flow" > < http:listener config-ref = "HTTP_Listener_Configuration" path = "/advanced/splitter" doc:name = "HTTP" > < splitter expression = "#[xpath3('//StockMovementDataRequest/StockMovementData',payload,'NODESET')]" doc:name = "Splitter" > < mulexml:dom-to-xml-transformer doc:name = "DOM to XML" > < logger level = "INFO" doc:name = "Logger" > < collection-aggregator failontimeout = "true" doc:name = "Collection Aggregator" > < dw:transform-message doc:name = "Transform Message" > < dw:set-payload > <!--[CDATA[%dw 1.0 %output application/json --- payload]]--> </ dw:set-payload > </ dw:transform-message > < logger message = "#[payload]" level = "INFO" doc:name = "Logger" > </ logger ></ collection-aggregator ></ logger ></ mulexml:dom-to-xml-transformer ></ splitter ></ http:listener ></ flow > |
The above flow, split the xml payload using the following xpath3 expression:
1 2 | < splitter expression = "#[xpath3('//StockMovementDataRequest/StockMovementData',payload,'NODESET')]" doc:name = "Splitter" > </ splitter > |
Complete Mule Flow Configuration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <!--xml version="1.0" encoding="UTF-8"?--> < mule xmlns:dw = "http://www.mulesoft.org/schema/mule/ee/dw" xmlns:tracking = "http://www.mulesoft.org/schema/mule/ee/tracking" xmlns:mulexml = "http://www.mulesoft.org/schema/mule/xml" xmlns:http = "http://www.mulesoft.org/schema/mule/http" xmlns = "http://www.mulesoft.org/schema/mule/core" xmlns:doc = "http://www.mulesoft.org/schema/mule/documentation" xmlns:spring = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd http://www.mulesoft.org/schema/mule/xml http://www.mulesoft.org/schema/mule/xml/current/mule-xml.xsd http://www.mulesoft.org/schema/mule/ee/tracking http://www.mulesoft.org/schema/mule/ee/tracking/current/mule-tracking-ee.xsd http://www.mulesoft.org/schema/mule/ee/dw http://www.mulesoft.org/schema/mule/ee/dw/current/dw.xsd"> < flow name = "use_xpath3_extract_value-Flow" > < http:listener config-ref = "HTTP_Listener_Configuration" path = "/advanced/xpath3" doc:name = "HTTP" > < mulexml:dom-to-xml-transformer doc:name = "DOM to XML" > < set-variable variablename = "position_id" value = "#[xpath3('/*:Create_Position_Response/*:Position_Reference/*:ID[2]')]" doc:name = "Variable" > < set-variable variablename = "position_id2" value = "#[xpath3('/*:Create_Position_Response/*:Position_Reference/*:ID[@*:type=\'Position_ID\']')]" doc:name = "Variable" > < logger message = "#[payload]" level = "INFO" doc:name = "Logger" > </ logger ></ set-variable ></ set-variable ></ mulexml:dom-to-xml-transformer ></ http:listener ></ flow > < flow name = "use_xpath3_splitter-Flow" > < http:listener config-ref = "HTTP_Listener_Configuration" path = "/advanced/splitter" doc:name = "HTTP" > < splitter expression = "#[xpath3('//StockMovementDataRequest/StockMovementData',payload,'NODESET')]" doc:name = "Splitter" > < mulexml:dom-to-xml-transformer doc:name = "DOM to XML" > < logger level = "INFO" doc:name = "Logger" > < collection-aggregator failontimeout = "true" doc:name = "Collection Aggregator" > < dw:transform-message doc:name = "Transform Message" > < dw:set-payload > <!--[CDATA[%dw 1.0 %output application/json --- payload]]--> </ dw:set-payload > </ dw:transform-message > < logger message = "#[payload]" level = "INFO" doc:name = "Logger" > </ logger ></ collection-aggregator ></ logger ></ mulexml:dom-to-xml-transformer ></ splitter ></ http:listener ></ flow > </ mule > |
Wednesday, March 1, 2017
Saturday, February 25, 2017
git: Procedures To Resolve Merge Conflict
GIT Command Line Cheat Sheet
1 2 3 4 | git branch -a #List branches git branch -- delete BRANCH_NAME #Delete a branch git checkout mybranch #switch to mybranch git merge FromBranch #merge FromBranch to the current working branch |
Introduction
This post is an introduction on how to resolve merge conflict. The following procedure will create a merge conflict
As shown in the following picture, the merge conflict is caused by that the same file was change at two different branches at different time. Typically, one develop check out the code from master branch, made some change and merged to the master branch. Another developer checkout the code after the previous developer and committed the code later.
Create Merge Conflicts
1 2 3 | $ mkdir groovy $ cd groovy $ vi basic_one.groovy |
Copy and paste the following code to the file of basic_one.grooy, and safe it (:wq)
1 2 3 4 5 | println( 'Hello World!!!' ) def myList = [ 1 , 3 , 4 , 5 , [ 10 , 13 ], 1 , 2 , 3 , [ 15 , 16 ], 22 , 33 ] println newList = myList.flatten() newList.each { item -> print item + " " } println() |
Now execute the following commands
1 2 3 | git init git add . git commit -am "first commit on master" |
So far we have commit a file to our local repository. Next, we will create a new branch, namely, develop. We will make changes to the same file
Now execute the following commands
1 2 3 4 | git branch develop # create develop branch git branch -a # list branches, you should see the * master and develop. # The start * indicates we are working on master git checkout develop # switch to develop branch |
1 | git branch -a |
Now, I see the following:
1 2 3 4 | Gary2013@Guojiangs-MacBook-Pro:~/atom-dev/groovy$ git branch -a * develop master Gary2013@Guojiangs-MacBook-Pro:~/atom-dev/groovy$ |
You can see that the current active branch is develop. We will change the content of the file to the following:
1 2 3 4 5 | println( 'Hello, Gary Liu!' ) def myList = [10, 13, 15, 16, 22, 33, 44, 55] println newList = myList.flatten(); newList.each { item -> print item + " " }; println(); |
Now execute the following commands to commit the develop branch:
1 | git commit -am "First commit to develop branch" |
In order to create conflicts, we will need to checkout the master branch and do some modification on the file
1 | git checkout master. |
If you execute: cat basic_one.groovy, you should see the following:
1 2 3 4 5 | println( 'Hello World!!!' ) def myList = [1, 3, 4, 5, [10, 13], 1, 2, 3, [15, 16], 22, 33] println newList = myList.flatten() newList.each { item -> print item + " " } println() |
This is where we left off. In the meantime, develop branch has changed. Let's make some change to the file and it will be looks like the following:
1 2 3 4 5 6 7 8 | println( 'Hello, World Again!' ); def myList = [-1, -2, 1, 3, 4, 5, [10, 13], 1, 2, 3, [15, 16], 22, 33] println newList = myList.flatten() newList.each { item -> print item + " " } println() |
Remember that we are at master branch now. We will merge from develop branch to the master
1 2 3 | git add . git commit -am "Second commit on master" git merge develop |
Now we see the following output:
1 2 3 4 5 | Gary2013@Guojiangs-MacBook-Pro:~/atom-dev/groovy$ git merge develop Auto-merging basic_one.groovy CONFLICT (content): Merge conflict in basic_one.groovy Automatic merge failed; fix conflicts and then commit the result. Gary2013@Guojiangs-MacBook-Pro:~/atom-dev/groovy$ |
We have successfully created merge conflict. If you can the file of basic_one.groovy, it looks like the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | Gary2013@Guojiangs-MacBook-Pro:~/atom-dev/groovy$ cat basic_one.groovy <<<<<<< HEAD println( 'Hello, World Again!' ); def myList = [-1, -2, 1, 3, 4, 5, [10, 13], 1, 2, 3, [15, 16], 22, 33] println newList = myList.flatten() newList.each { item -> print item + " " } println() ======= println( 'Hello, Gary Liu!' ) def myList = [10, 13, 15, 16, 22, 33, 44, 55] println newList = myList.flatten(); newList.each { item -> print item + " " }; println(); >>>>>>> develop Gary2013@Guojiangs-MacBook-Pro:~/atom-dev/groovy$ |
Resolve Merge Conflicts
The above case is a bit extreme, as the whole file is almost changed. The HEAD section is from master branch and the lower part is from develop. To resolve the merge conflict, we need to edit file. For this case I will just take the master case, and the resultant file looks like the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Gary2013@Guojiangs-MacBook-Pro:~/atom-dev/groovy$ vi basic_one.groovy Gary2013@Guojiangs-MacBook-Pro:~/atom-dev/groovy$ git commit -am "Resolve merge conflict" [master d2d5ecf] Resolve merge conflict Gary2013@Guojiangs-MacBook-Pro:~/atom-dev/groovy$ git merge develop Already up-to-date. Gary2013@Guojiangs-MacBook-Pro:~/atom-dev/groovy$ cat basic_one.groovy println( 'Hello, World Again!' ); def myList = [-1, -2, 1, 3, 4, 5, [10, 13], 1, 2, 3, [15, 16], 22, 33] println newList = myList.flatten() newList.each { item -> print item + " " } println() Gary2013@Guojiangs-MacBook-Pro:~/atom-dev/groovy$ |
To resolve merge conflict, we have to manually modify the file. We can use IDE like eclipse, sourceTree, etc, but we have to make decisions on which one is the best. After that we need to commit the changes and do a merge again to make sure that all the conflicts are addressed correctly.
Thursday, February 23, 2017
Mule Dev Trick: Using Message Properties Transformer
The Abuse of set-variable
Many places I saw the code like the following:Basically, developers set a bunch of flow variables for later usage. First of all, this kind of situation reflects design flaws. We should ask our selves why do we need to set so many flow variables? Can we use bean instead?
Use Message Property Transformer
Mule platform provide a very powerful transformer, namely message property transformer. Here is an example code:1 2 3 4 5 6 7 8 | < message-properties-transformer doc:name = "Add Flow Vars" scope = "invocation" > < add-message-property key = "var_abd" value = "#['abd112233']" > < add-message-property key = "var_one" value = "#['abd111111']" > < add-message-property key = "var_two" value = "#['abd111222']" > < add-message-property key = "var_three" value = "#['abd111333']" > < rename-message-property key = "var_aaa" value = "mysql_db_name" > < delete-message-property key = "var_xyz" > </ delete-message-property ></ rename-message-property ></ add-message-property ></ add-message-property ></ add-message-property ></ add-message-property ></ message-properties-transformer > |
invocation: flow variables outbound: property session: session variablesBy using message property transformer, you can simplify the design, and make the flow easier to review.
Thursday, February 9, 2017
Mule Dev Trick: Load A Text File And Convert To ArrayList
The Challenge
Here is the task. I have a file like the following:AAAA BBBB CCCC DDDDI need to convert this content to an ArrayList, so that I can look through them. Actually each line is an object name. I need to query those objects from web services.
Solution
Here are the steps:- Load file and payload as string. Mule provide a construct called "parse-template", which can be used to load file. This construct will load the file and convert the content to a string payload.
- Use Groovy or MEL to convert the string to an ArrayList
1 2 3 4 5 6 7 8 9 | < parse-template location = "veeva_objects.txt" doc:name = "Parse Template" > </ parse-template > < set-payload value = "#[Arrays.asList(payload.trim().replaceAll("[\\t\\n\\r]", ",").split(","))]" doc:name = "Set Payload" > </ set-payload > < scripting:component doc:name = "Groovy" > < scripting:script engine = "Groovy" > <!--[CDATA[return payload.trim().replaceAll('[\\t\\n\\r]', ',').split(',').collect{ it.trim()}]]--> </ scripting:script > </ scripting:component > |
Key Learning Points
Few points we learn from this:- MEL support java directly. This is really very powerful.
- Groovy is very powerful tool in Mule application development. collect {it.trim()} is similar as java stream function.
Anypoint Studio Error: The project is missing Munit lIbrary to run tests
Anypoint Studio 7.9 has a bug. Even if we following the article: https://help.mulesoft.com/s/article/The-project-is-missing-MUnit-libraries-...
-
Introduction MuleSoft has changed many stuff for the last year. One of the questions developer will ask is how to change the logging level....
-
Introduction The video for this blog is available at: http://www.youtube.com/watch?v=wVCmik-2xAM&feature=youtu.be Mule and JBoss ...
-
Congratulation Gary! First of all, I must congratulate myself for getting this done. For almost a year, I have been thinking to take the e...