Introduction
In the following we are going to set up a dynamic configurable Camel route whose configuration can be changed during runtime without restarting the route. You may think that this sounds pretty easy but it turned out that it is not. So let me introduce you to the problem.Usually property placeholder are used to read properties from a configuration file. These properties can be used smoothly within route configuration but when the property is changed the update mechanism has no effect on the running route. The route has to be restarted to tighten the change. An automatic route restart can be achieved by configuring the appropriate update strategy within the property placeholder configuration. An example of this approach can be found in the Apache Camel documentation. I will quote the relevant snippet in the following.
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
<!-- OSGI blueprint property placeholder -->
<cm:property-placeholder id="myblueprint.placeholder" persistent-id="camel.blueprint">
<!-- list some properties as needed -->
<cm:default-properties>
<cm:property name="result" value="mock:result"/>
</cm:default-properties>
</cm:property-placeholder>
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<!-- in the route we can use {{ }} placeholders which will lookup in blueprint
as Camel will auto detect the OSGi blueprint property placeholder and use it -->
<route>
<from uri="direct:start"/>
<to uri="mock:foo"/>
<to uri="{{result}}"/>
</route>
</camelContext>
</blueprint>
The problem is that a route restart on a property change is often not acceptable as unintentional updates are triggered. So if you are okay with the restart, use the snippet from the Camel documentation if you want to get around restarting the route keep reading.Prerequesites
- Installed Maven
- Installed Apache Karaf (4.x.x) or Talend ESB (6.x.x)
Guide
As simple use case we just want to respond to messages from certain countries. The origin of the message can be identified by checking the country header. The list of countries must be configurable as our foreign policy interests may change in the future. Of course a route restart is not acceptable.
In the following we are going to use the Recipient List pattern combined with a Managed Service to get around restarting the route. The Recipient List pattern allows us to route messages to a number of dynamically specified recipients. For this particular use case the recipient list will always contain exactly one recipient but the endpoint is constructed dynamically at runtime! When using the Recipient List pattern recipientList replaces to within the Camel DSL. The recipient list can be built in various ways. In our case a Java Bean called MessageFilter is used. The MessageFilter decides either to forward the message to the output route or to put it into the trash route.
In the following we are going to use the Recipient List pattern combined with a Managed Service to get around restarting the route. The Recipient List pattern allows us to route messages to a number of dynamically specified recipients. For this particular use case the recipient list will always contain exactly one recipient but the endpoint is constructed dynamically at runtime! When using the Recipient List pattern recipientList replaces to within the Camel DSL. The recipient list can be built in various ways. In our case a Java Bean called MessageFilter is used. The MessageFilter decides either to forward the message to the output route or to put it into the trash route.
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<route id="input">
<from uri="vm:input"/>
<!-- We use the messageRouter Bean which will call routeTo method to get the recipient.
The recipient is either output or trash depending on the header. This allows us dynamic
filtering within camel routes without restarting the route. -->
<recipientList>
<method ref="messageFilter" method="routeTo(${header.country})"/>
</recipientList>
</route>
<!-- Throws the trash away. -->
<route id="trash">
<from uri="vm:trash"/>
<stop/>
</route>
<!-- Prints everything which ends up in output. -->
<route id="output">
<from uri="vm:output"/>
<to uri="stream:out"/>
</route>
</camelContext>
The action what should happen when Configuration Admin update happens depends on the implementation. We simply reinitialize the list of countries. That's all that needs to be done.
public class MessageFilter implements ManagedService {
private List<String> countries = Arrays.asList("GER", "LUX");
public String routeTo(String country) {
if (countries.contains(country)) {
return "vm:output";
} else {
return "vm:trash";
}
}
public void updated(Dictionary<String, ?> dictionary) throws ConfigurationException {
if(null == dictionary) {
return;
}
countries = Arrays.asList(
String.valueOf(dictionary
.get("countries"))
.replaceAll("\\s+", "")
.split(","));
}
}
Keine Kommentare:
Kommentar veröffentlichen