Monday, March 30, 2015

Apache Camel Property Placeholders - Commons Configuration in Raspberry Pi

The last set of changes I made were adding Apache Camel to my PiHexJ code. I added support for a camel context running on the Raspberry Pi. It's fairly easy to get Camel running on the Pi however there is a question I guess I need to answer first.

Why add apache camel to my robot?

There are a number of reasons why I chose to add Camel to the Robot's software stack. Some good reasons, some not so good reasons.  I'll let you judge which is which, probably better than trying to argue one way or another. 
  • A need to integrate for control. I intend to have multiple ways of controlling the robot, via rest interface, by radio control and by the robot sending sensor info to a server to get instructions from the server. 
  • A need to integrate with persistence to read calibration and configuration data.
  • I use camel at my day job and figured it would be good to gain more experience with it using it in a different way.
  • I wanted to see if camel could run well on a raspberry pi.

Running Apache Camel on the Raspberry Pi

Camel can be run in number of different ways. It is most effective when it has a supporting dependency injection framework. In the past I've run it three different ways;
  • OSGI containers using Aries Blueprint
  • With Spring xml
  • Using Guice
  • Directly from java main.
Camel routes can be configured in via xml, however I never have like that approach even though many examples are provided that way. I prefer the Java dsl.
In my raspberry pi application I am using Apache Commons Configuration to abstract my configuration and Guice for dependency injection. I don't have any plans to run an OSGI container on the Pi so that rules our using blueprint. Spring was never on my list as an option so that leaves Guice and running from the Java main.

The obvious choice is Guice. I wanted to be able to use property placeholders in my routes. I like the flexibility of using them. What I wanted is the properties to resolve against the commons configuration since I am using configuration to encapsulate my config. I also needed beans to be bindable via guice and accessible in the camel registry. For example for the camel-guava-eventbus component. I'm using eventbus to provide a bus to allow different components to send events that I can react to and affect the  robots behavior. It gives me a more consistent way to manage the event processing.

Bridging between Commons Configuration, Guice and the Camel Registry

Camel has a guice component. It provides some excellent support. I would suggest it's the correct way to do this however I wanted to experiment with camel a bit more and decided just to roll my own camel context. One of the things I've learned about camel is there are many ways to solve each problem. So many in fact I'd recommend +Scott Cranton book the Apache Camel Developers Cookbook it helps you narrow down approaches to solving the types of real world integration problems you are likely to encounter. Since this is not a real world problem lets crack on (or should that be hack on).

Two problems to solve:
1. Property resolvers using Commons Configuration
2. Binding beans to the camel registry


1. Property Resolution

Camel provides a very simple mechanism for resolving additional properties. They provide an interface for PropertiesFunction. These are used to provide additional properties lookups. For example from the camel documentation:
The service function is for looking up a service which is defined using OS environment variables using the service naming idiom, to refer to a service location using hostname : port
  • NAME_SERVICE_HOST
  • NAME_SERVICE_PORT
in other words the service uses _SERVICE_HOST and _SERVICE_PORT as prefix. So if the service is named FOO, then the OS environment variables should be set as
export $FOO_SERVICE_HOST=myserver
export $FOO_SERVICE_PORT=8888

For example if the FOO service a remote HTTP service, then we can refer to the service in the Camel endpoint uri, and use the HTTP component to make the HTTP call:
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  <route>
    <from uri="direct:start"/>
    <to uri="http://{{service:FOO}}/myapp"/>
  </route>
</camelContext>

I could take advantage of this to take a shortcut and created very simple ConfigurationPropertiesFunction. This function provides another type config that can be used in properties placeholders as {{config:myConfigProperty}}. This then uses the function to resolve the property placeholder in the commons configuration object.

public class ConfigurationPropertiesFunction implements PropertiesFunction {

    private static final String NAME = "config";
    private Configuration config;

    @Inject
    public ConfigurationPropertiesFunction(Configuration config){
        this.config = config;
    }

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public String apply(String remainder) {
        return config.getString(remainder);
    }
}

This simple function allows me to use apache commons configuration with my camel property placeholders directly. My configuration uses a configuration file in the resources folder. This makes testing simple as I don't need to override static test properties as tests use the test properties in the test resources folder. The only thing I do is use a custom Camel Context extending from DefaultCamelContext and adding a single method to add my function.

    @Inject
    public void setConfigurationPropertiesParser(Configuration configuration){
        log.info("Setting properties parser");
        PropertiesComponent pc = super.getComponent("properties", PropertiesComponent.class);
        pc.addFunction(new ConfigurationPropertiesFunction(config));
    }

The next step is to create this properties component and register it in the registry rather than getting the properties component and adding it this way. Having a way to abstract configuration outside of spring or blueprint is useful. Guice provides support for this but using guice to manage properties and inject them into camel as a series of named strings is probably not the most appropriate way to use guice.

Testing the new properties function

The following test case validates that the resolver works:

    protected Injector injector = Guice.createInjector(new PihexModule());

    @Override
    protected CamelContext createCamelContext() throws Exception {
        return injector.getInstance(CamelContext.class);
    }

    @Override
    protected RouteBuilder createRouteBuilder() throws Exception {
        Configuration config = injector.getInstance(Configuration.class);
        config.setProperty("testProperty", "testValue");

        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from("direct:test")
                        .setBody(simple("{{config:testProperty}}"))
                        .to("mock:mock");
            }
        };
    }
    /**
     * test the properties place holder by setting a value in the config and testing
     * we can use a placeholder in a camel context
     */
    @Test
    public void testPlaceholder() throws Exception{
        MockEndpoint mock = getMockEndpoint("mock:mock");
        template.send("direct:test", createExchangeWithBody(""));
        mock.expectedMessageCount(1);

        Exchange exchange = mock.getExchanges().get(0);
        mock.expectedMessageCount(1);
        assertMockEndpointsSatisfied(1, TimeUnit.SECONDS);

        assertEquals("testValue", exchange.getIn().getBody(String.class));
    }

2. Binding beans to the camel registry

In the next post I'll cover how I did the bean binding. Spoiler alert since I was hacking around my own camel context I just stuck with it and used my context. I did not create a dependency on camel-guice.

1 comment: