For some while I have been thinking about how to be able to develop reusable Mule configuration files containing, for instance, entire flows.
Of course I have seen the strategy that uses property files and the Spring PropertyPlaceholderConfigurer to inject different values into Mule components depending on the values from the property files. While providing some flexibility, I am not entirely fond of this strategy because:
- I need to insert property placeholders in the Mule configuration where I want to be able to configure the flow.
If I need to add the opportunity to configure another aspect of a flow and its components, I would need to add another property placeholder. I would also have to go through all the Mule applications which uses the old version of the reusable flow and add the new property to their configuration files.
- I am only able to add property placeholders where the Mule XML schemas allow me to specify configuration values or where I am able to inject property values using the Spring dependency injection mechanism.
In a previous developer-life I developed Java applications using Spring and used to use the Spring PropertyOverrideConfigurer to modify properties of Spring beans when I wanted to perform smaller modifications that did not warrant overriding entire beans.
In addition, I have heard rumours that Mule configurations are really Spring configurations with custom namespaces (somewhat simplified I suspect), so I thought that I should try to use this approach with Mule configurations.
I’ve procrastinated enough, let’s look at some code, or at least some Mule configuration:
<?xml version="1.0" encoding="UTF-8"?> <mule xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns:spring="http://www.springframework.org/schema/beans" xmlns:core="http://www.mulesoft.org/schema/mule/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd 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 "> <flow name="HelloWorldFlow"> <http:inbound-endpoint name="inboundHttpEndpoint" exchange-pattern="request-response" host="localhost" port="8081"/> <set-payload name="payloadSetter" value="#['Hello World']" /> </flow> <spring:beans> <spring:bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer"> <spring:property name="properties"> <spring:map> <spring:entry key="payloadSetter.value" value="#['Hello Developer World']"/> <spring:entry key="inboundHttpEndpoint.endpointBuilder.port" value="8123"/> </spring:map> </spring:property> </spring:bean> </spring:beans> </mule>
First of all, I want to say that in a real-world application the above configuration file would be split into two files; the part containing the flow would be located in some library while the second part containing the modification to the flow would be located in a file in an application that uses the library containing the flow.
The first part of the above Mule configuration file is a normal Mule flow with the name “HelloWorldFlow”. It exposes a HTTP inbound endpoint on localhost:8081 that, for every request that arrives, sets the response payload to the string “Hello World”.
Note that both the inbound endpoint and the set-payload component both have a name attribute specified.
The second part is where it becomes interesting:
A regular Spring bean being an instance of the Spring class PropertyOverrideConfigurer is defined. A map is injected into the properties property of this bean.
Each entry in the map consists of a key and a value – that’s what maps usually contain.
A key typically consists of a bean name, a dot and the name of a property on the bean. The value contains the (new) value that is to be injected into the bean and property specified by the key.
The first entry in the map in the above example injects the value “#[‘Hello Developer World’]” into the value property on the bean with the name payloadSetter.
Recall that payloadSetter was the name we gave the Mule component that set the payload in the Mule flow we looked at earlier.
This was easy – a name attribute on the Mule component and the value attribute which corresponds to a bean property!
Regretfully, it is not always that simple, as we will see in the next entry in the map:
The value to be injected is “8123”, which is the new port that the endpoint is to listen on. The first part of the key still match the name of the Mule component that we want to modify – in this case the HTTP inbound endpoint component.
If you try to set the key to “inboundHttpEndpoint.port” you will get an exception that tell you that there is no such property on the bean of the class InboundEndpointFactoryBean.
Note the last part of the bean class name, “FactoryBean”. This is a type of Spring beans that are factories that create beans when they are requested (see the Spring API documentation of the org.springframework.beans.factory.FactoryBean interface for more information).
The port to use in the HTTP inbound endpoint is not present on the factory bean, but instead in an object accessible through the endpointBuilder property on the factory bean and, in turn, in the port property of that object.
If the above Mule configuration file is executed in a Mule server on your computer, a request to the URL http://localhost:8123 will result in the string “Hello Developer World” being displayed in the browser.
Before wrapping this article up, I want to mention that the PropertyOverrideConfigurer also supports reading properties from files, enabling you to contain modifications in a separate file.
We have seen that the standard, at least to me, Spring mechanism using the PropertyOverrideConfigurer can indeed be applied to Mule components, albeit sometimes a little bit of detective work is required in order to find the names of the property names to use.
A drawback of this approach is that there is a risk that classes are modified and property names become invalid. In such a case, the PropertyOverrideConfigurer will throw an exception unless its ignoreInvalidKeys property is set to true.