Working with Camel and SCR

SCR stands for Service Component Runtime and is an implementation of OSGi Declarative Services specification. SCR enables any plain old Java object to expose and use OSGi services with no boilerplate code.

OSGi framework knows your object by looking at SCR descriptor files in its bundle which are typically generated from Java annotations by a plugin such as org.apache.felix:maven-scr-plugin.

Running Camel in an SCR bundle is a great alternative for Spring DM and Blueprint based solutions having significantly fewer lines of code between you and the OSGi framework. Using SCR your bundle can remain completely in Java world; there is no need to edit XML or properties files. This offers you full control over everything and means your IDE of choice knows exactly what is going on in your project.

Camel SCR support

Available as of Camel 2.15.0

 

Camel-scr bundle is not included in Apache Camel versions prior 2.15.0, but the artifact itself can be used with any Camel version since 2.12.0.

 

org.apache.camel/camel-scr bundle provides a base class, AbstractCamelRunner, which manages a Camel context for you and a helper class, ScrHelper, for using your SCR properties in unit tests. Camel-scr feature for Apache Karaf defines all features and bundles required for running Camel in SCR bundles.

AbstractCamelRunner class ties CamelContext's lifecycle to Service Component's lifecycle and handles configuration with help of Camel's PropertiesComponent. All you have to do to make a Service Component out of your java class is to extend it from AbstractCamelRunner and add the following org.apache.felix.scr.annotations on class level:

 

Add required annotations

 

Then implement getRouteBuilders() method which returns the Camel routes you want to run:

 

Implement getRouteBuilders()

 

And finally provide the default configuration with:

 

Default configuration in annotations

 

That's all. And if you used camel-archetype-scr to generate a project all this is already taken care of.

Below is an example of a complete Service Component class, generated by camel-archetype-scr:

 

CamelScrExample.java

 

CamelContextId and active properties control the CamelContext's name (defaults to "camel-runner-default") and whether it will be started or not (defaults to "false"), respectively. In addition to these you can add and use as many properties as you like. Camel's PropertiesComponent handles recursive properties and prefixing with fallback without problem.

AbstractCamelRunner will make these properties available to your RouteBuilders with help of Camel's PropertiesComponent and it will also inject these values into your Service Component's and RouteBuilder's fields when their names match. The fields can be declared with any visibility level, and many types are supported (String, int, boolean, URL, ...).

Below is an example of a RouteBuilder class generated by camel-archetype-scr:

 

CamelScrExampleRoute.java

 

Let's take a look at CamelScrExampleRoute in more detail.

 

The values of these fields are set with values from properties by matching their names.

 

If you need to add some beans to CamelContext's registry for your routes, you can do it like this.

 

It is a good idea to check that required parameters are set and they have meaningful values before allowing the routes to start.

 

Note that pretty much everything in the route is configured with properties. This essentially makes your RouteBuilder a template. SCR allows you to create more instances of your routes just by providing alternative configurations. More on this in section Using Camel SCR bundle as a template.

AbstractCamelRunner's lifecycle in SCR

  1. When component's configuration policy and mandatory references are satisfied SCR calls activate(). This creates and sets up a CamelContext through the following call chain: activate() → prepare() → createCamelContext() → setupPropertiesComponent() → configure() → setupCamelContext(). Finally, the context is scheduled to start after a delay defined in AbstractCamelRunner.START_DELAY with runWithDelay().
  2. When Camel components (ComponentResolver services, to be exact) are registered in OSGi, SCR calls gotCamelComponent() which reschedules/delays the CamelContext start further by the same AbstractCamelRunner.START_DELAY. This in effect makes CamelContext wait until all Camel components are loaded or there is a sufficient gap between them. The same logic will tell a failed-to-start CamelContext to try again whenever we add more Camel components.
  3. When Camel components are unregistered SCR calls lostCamelComponent(). This call does nothing.
  4. When one of the requirements that caused the call to activate() is lost SCR will call deactivate(). This will shutdown the CamelContext.

In (non-OSGi) unit tests you should use prepare() → run() → stop() instead of activate() → deactivate() for more fine-grained control. Also, this allows us to avoid possible SCR specific operations in tests.

Using camel-archetype-scr

The easiest way to create an Camel SCR bundle project is to use camel-archetype-scr and Maven.

You can generate a project with the following steps:

Generating a project

Done!

Now run:

and the bundle is ready to be deployed.

Unit testing Camel routes

Service Component is a POJO and has no special requirements for (non-OSGi) unit testing. There are however some techniques that are specific to Camel SCR or just make testing easier.

Below is an example unit test, generated by camel-archetype-scr:


CamelScrExampleTest.java

 

Now, let's take a look at the interesting bits one by one.

 

Using property prefixing

This allows you to override parts of the configuration by prefixing properties with "unit.". For example, unit.from overrides from for the unit test.

Prefixes can be used to handle the differences between the runtime environments where your routes might run. Moving the unchanged bundle through development, testing and production environments is a typical use case.

 

Getting test configuration from annotations

Here we configure the Service Component in test with the same properties that would be used in OSGi environment.

 

Mocking components for test

Components that are not available in test can be mocked like this to allow the route to start.

 

Adjusting routes for test

Camel's AdviceWith feature allows routes to be modified for test.

 

Starting the routes

Here we start the Service Component and along with it the routes.

 

Sending a test message

Here we send a message to a route in test.

Running the bundle in Apache Karaf

Once the bundle has been built with mvn install it's ready to be deployed. To deploy the bundle on Apache Karaf perform the following steps on Karaf command line:

Deploying the bundle in Apache Karaf

Overriding the default configuration

By default, Service Component's configuration PID equals the fully qualified name of its class. You can change the example bundle's properties with Karaf's config:* commands:

Override a property

Or you can change the configuration by editing property files in Karaf's etc folder.

Using Camel SCR bundle as a template

Let's say you have a Camel SCR bundle that implements an integration pattern that you use frequently, say, from → to, with success/failure logging and redelivery which also happens to be the pattern our example route implements. You probably don't want to create a separate bundle for every instance. No worries, SCR has you covered.

Create a configuration PID for your Service Component, but add a tail with a dash and SCR will use that configuration to create a new instance of your component.

Creating a new Service Component instance

This will start a new CamelContext with your overridden properties. How convenient.

When designing a Service Component to be a template you typically don't want it to start without a "tailed" configuration i.e. with the default configuration.

To prevent your Service Component from starting with the default configuration add policy = ConfigurationPolicy.REQUIRE to the class level @Component annotation.

© 2004-2015 The Apache Software Foundation.
Apache Camel, Camel, Apache, the Apache feather logo, and the Apache Camel project logo are trademarks of The Apache Software Foundation. All other marks mentioned may be trademarks or registered trademarks of their respective owners.
Graphic Design By Hiram