Pages

Friday, August 2, 2013

Nested Properties in Camel Routes

Apache Camel does not support Spring's nested properties in its configuration. I believe this feature is coming up, but at the time of writing this it's neither the case in the version we're running (2.9.1) nor in the current version (2.10.6 seemed totally broken, 2.10.5 also doesn't support it). This is generally unfortunate but in our case we're particularly missing this feature for the autoStartup property of a route.

We usually have groups of routes that are either started together or not at all. However, for e.g. debugging it would be nice to turn the whole group off and specific routes in the group on. This can be achieved relatively easily with a simple helper class that gets properties injected by Spring, hence supporting nested properties.


So the general idea is to have a properties file like such:
routes.group1.enabled=true
routes.group1.route1.enabled=${routes.group1.enabled}
routes.group1.route2.enabled=${routes.group1.enabled}
routes.group1.route3.enabled=${routes.group1.enabled}
routes.group1.route4.enabled=${routes.group1.enabled}
routes.group1.route5.enabled=${routes.group1.enabled}
routes.group1.route6.enabled=${routes.group1.enabled}

This way the whole group could be turned off by setting routes.group1.enabled=false and the route that we want to observe/debug could be turned on with e.g. routes.group1.route4.enabled=true. Of course that could be achieved by just toggling all properties. But if there is another properties file overwriting some of the values (which we have due to different environments or pre-packaged core jars) this could be tedious to do. Also using JVM parameters like -Droutes.group1.route4.enabled=true could get rather lengthly.

So I created the following little helper class that gets the CamelContext and a map of route IDs and startup settings injected:

package codelearnings;

import java.util.HashMap;
import java.util.List;

import org.apache.camel.model.ModelCamelContext;
import org.apache.camel.model.RouteDefinition;

public class RouteManager {
 
 private ModelCamelContext camelContext;
 private HashMap<String, Boolean>startupConfig=new HashMap<String, Boolean>();
 private Boolean autoStartupDefault=false;

 public RouteManager(ModelCamelContext camelContext) {
  this.camelContext = camelContext;
 }
 
 public void setAutoStartupDefault(Boolean autoStartupDefault) {
  this.autoStartupDefault = autoStartupDefault;
 }

 public void setStartupConfig(HashMap<String, Boolean> startupConfig) {
  this.startupConfig = startupConfig;
 }

 public void executeStartupConfig() throws Exception {
  List<RouteDefinition>defs=camelContext.getRouteDefinitions();
  for (RouteDefinition def : defs) {
   if (def.getId()!=null) {
    Boolean startup=null;
    if (startupConfig.containsKey(def.getId())) {
     startup=startupConfig.get(def.getId());
    } else {
     startup=autoStartupDefault;
    }
     
    if (startup!=null) {
     def.setAutoStartup(Boolean.toString(startup));
       
     if (startup && def.isStartable(camelContext)) {
      camelContext.startRoute(def.getId());
     }
     if (!startup && def.isStoppable(camelContext)) {
      camelContext.stopRoute(def.getId());
     }      
    }    
   }
  }  
 }
}

It also allows to set a default for all routes that are not listed. If the default is null unconfigured routes are left alone. In the application context the RouteManager bean is configured as below and the init-method executeStartupConfig executed.

<bean id="routeManager"  class="codelearnings.RouteManager" init-method="executeStartupConfig">
    <constructor-arg ref="myCamelContext"/>
    <property name="autoStartupDefault"><null/></property>
    <property name="startupConfig">
        <map>
            <entry key="route1" value="${routes.group1.route1.enabled}">
            <entry key="route2" value="${routes.group1.route2.enabled}">
            <entry key="route3" value="${routes.group1.route3.enabled}">
            <entry key="route4" value="${routes.group1.route4.enabled}">
            <entry key="route5" value="${routes.group1.route5.enabled}">
            <entry key="route6" value="${routes.group1.route6.enabled}">
        </map>
    </property>
</bean>

Here you go, at least for route start-up you can use nested properties, since they are resolved by Spring and not by Camel.

No comments:

Post a Comment