Since we're on a major migration process of this website, some component documents here are out of sync right now. In the meantime you may want to look at the early version of the new website
https://camel.apache.org/staging/
We would very much like to receive any feedback on the new site, please join the discussion on the Camel user mailing list.
Working with Camel and SCRSCR 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 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 supportAvailable 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.
Add required annotations @Component @References({ @Reference(name = "camelComponent",referenceInterface = ComponentResolver.class, cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, bind = "gotCamelComponent", unbind = "lostCamelComponent") })
Then implement
Implement getRouteBuilders() @Override protected List<RoutesBuilder> getRouteBuilders() { List<RoutesBuilder> routesBuilders = new ArrayList<>(); routesBuilders.add(new YourRouteBuilderHere(registry)); routesBuilders.add(new AnotherRouteBuilderHere(registry)); return routesBuilders; }
And finally provide the default configuration with:
Default configuration in annotations @Properties({ @Property(name = "camelContextId", value = "my-test"), @Property(name = "active", value = "true"), @Property(name = "...", value = "..."), ... })
That's all. And if you used Below is an example of a complete Service Component class, generated by
CamelScrExample.java // This file was generated from org.apache.camel.archetypes/camel-archetype-scr/2.15-SNAPSHOT package example; import java.util.ArrayList; import java.util.List; import org.apache.camel.scr.AbstractCamelRunner; import example.internal.CamelScrExampleRoute; import org.apache.camel.RoutesBuilder; import org.apache.camel.spi.ComponentResolver; import org.apache.felix.scr.annotations.*; @Component(label = CamelScrExample.COMPONENT_LABEL, description = CamelScrExample.COMPONENT_DESCRIPTION, immediate = true, metatype = true) @Properties({ @Property(name = "camelContextId", value = "camel-scr-example"), @Property(name = "camelRouteId", value = "foo/timer-log"), @Property(name = "active", value = "true"), @Property(name = "from", value = "timer:foo?period=5000"), @Property(name = "to", value = "log:foo?showHeaders=true"), @Property(name = "messageOk", value = "Success: {{from}} -> {{to}}"), @Property(name = "messageError", value = "Failure: {{from}} -> {{to}}"), @Property(name = "maximumRedeliveries", value = "0"), @Property(name = "redeliveryDelay", value = "5000"), @Property(name = "backOffMultiplier", value = "2"), @Property(name = "maximumRedeliveryDelay", value = "60000") }) @References({ @Reference(name = "camelComponent",referenceInterface = ComponentResolver.class, cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, bind = "gotCamelComponent", unbind = "lostCamelComponent") }) public class CamelScrExample extends AbstractCamelRunner { public static final String COMPONENT_LABEL = "example.CamelScrExample"; public static final String COMPONENT_DESCRIPTION = "This is the description for camel-scr-example."; @Override protected List<RoutesBuilder> getRouteBuilders() { List<RoutesBuilder> routesBuilders = new ArrayList<>(); routesBuilders.add(new CamelScrExampleRoute(registry)); return routesBuilders; } }
Below is an example of a RouteBuilder class generated by
CamelScrExampleRoute.java // This file was generated from org.apache.camel.archetypes/camel-archetype-scr/2.15-SNAPSHOT package example.internal; import org.apache.camel.LoggingLevel; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.impl.SimpleRegistry; import org.apache.commons.lang.Validate; public class CamelScrExampleRoute extends RouteBuilder { SimpleRegistry registry; // Configured fields private String camelRouteId; private Integer maximumRedeliveries; private Long redeliveryDelay; private Double backOffMultiplier; private Long maximumRedeliveryDelay; public CamelScrExampleRoute(final SimpleRegistry registry) { this.registry = registry; } @Override public void configure() throws Exception { checkProperties(); // Add a bean to Camel context registry registry.put("test", "bean"); errorHandler(defaultErrorHandler() .retryAttemptedLogLevel(LoggingLevel.WARN) .maximumRedeliveries(maximumRedeliveries) .redeliveryDelay(redeliveryDelay) .backOffMultiplier(backOffMultiplier) .maximumRedeliveryDelay(maximumRedeliveryDelay)); from("{{from}}") .startupOrder(2) .routeId(camelRouteId) .onCompletion() .to("direct:processCompletion") .end() .removeHeaders("CamelHttp*") .to("{{to}}"); from("direct:processCompletion") .startupOrder(1) .routeId(camelRouteId + ".completion") .choice() .when(simple("${exception} == null")) .log("{{messageOk}}") .otherwise() .log(LoggingLevel.ERROR, "{{messageError}}") .end(); } } public void checkProperties() { Validate.notNull(camelRouteId, "camelRouteId property is not set"); Validate.notNull(maximumRedeliveries, "maximumRedeliveries property is not set"); Validate.notNull(redeliveryDelay, "redeliveryDelay property is not set"); Validate.notNull(backOffMultiplier, "backOffMultiplier property is not set"); Validate.notNull(maximumRedeliveryDelay, "maximumRedeliveryDelay property is not set"); } }
Let's take a look at
// Configured fields private String camelRouteId; private Integer maximumRedeliveries; private Long redeliveryDelay; private Double backOffMultiplier; private Long maximumRedeliveryDelay; The values of these fields are set with values from properties by matching their names.
// Add a bean to Camel context registry registry.put("test", "bean"); If you need to add some beans to CamelContext's registry for your routes, you can do it like this.
public void checkProperties() { Validate.notNull(camelRouteId, "camelRouteId property is not set"); Validate.notNull(maximumRedeliveries, "maximumRedeliveries property is not set"); Validate.notNull(redeliveryDelay, "redeliveryDelay property is not set"); Validate.notNull(backOffMultiplier, "backOffMultiplier property is not set"); Validate.notNull(maximumRedeliveryDelay, "maximumRedeliveryDelay property is not set"); } It is a good idea to check that required parameters are set and they have meaningful values before allowing the routes to start.
from("{{from}}") .startupOrder(2) .routeId(camelRouteId) .onCompletion() .to("direct:processCompletion") .end() .removeHeaders("CamelHttp*") .to("{{to}}"); from("direct:processCompletion") .startupOrder(1) .routeId(camelRouteId + ".completion") .choice() .when(simple("${exception} == null")) .log("{{messageOk}}") .otherwise() .log(LoggingLevel.ERROR, "{{messageError}}") .end(); 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
In (non-OSGi) unit tests you should use Using camel-archetype-scrThe easiest way to create an Camel SCR bundle project is to use You can generate a project with the following steps: Generating a project $ mvn archetype:generate -Dfilter=org.apache.camel.archetypes:camel-archetype-scr Choose archetype: 1: local -> org.apache.camel.archetypes:camel-archetype-scr (Creates a new Camel SCR bundle project for Karaf) Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 1 Define value for property 'groupId': : example [INFO] Using property: groupId = example Define value for property 'artifactId': : camel-scr-example Define value for property 'version': 1.0-SNAPSHOT: : Define value for property 'package': example: : [INFO] Using property: archetypeArtifactId = camel-archetype-scr [INFO] Using property: archetypeGroupId = org.apache.camel.archetypes [INFO] Using property: archetypeVersion = 2.15-SNAPSHOT Define value for property 'className': : CamelScrExample Confirm properties configuration: groupId: example artifactId: camel-scr-example version: 1.0-SNAPSHOT package: example archetypeArtifactId: camel-archetype-scr archetypeGroupId: org.apache.camel.archetypes archetypeVersion: 2.15-SNAPSHOT className: CamelScrExample Y: : Done! Now run: mvn install and the bundle is ready to be deployed. Unit testing Camel routesService 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 CamelScrExampleTest.java // This file was generated from org.apache.camel.archetypes/camel-archetype-scr/2.15-SNAPSHOT package example; import java.util.List; import org.apache.camel.scr.internal.ScrHelper; import org.apache.camel.builder.AdviceWithRouteBuilder; import org.apache.camel.component.mock.MockComponent; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.model.ModelCamelContext; import org.apache.camel.model.RouteDefinition; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class CamelScrExampleTest { Logger log = LoggerFactory.getLogger(getClass()); @Rule public TestName testName = new TestName(); CamelScrExample integration; ModelCamelContext context; @Before public void setUp() throws Exception { log.info("*******************************************************************"); log.info("Test: " + testName.getMethodName()); log.info("*******************************************************************"); // Set property prefix for unit testing System.setProperty(CamelScrExample.PROPERTY_PREFIX, "unit"); // Prepare the integration integration = new CamelScrExample(); integration.prepare(null, ScrHelper.getScrProperties(integration.getClass().getName())); context = integration.getContext(); // Disable JMX for test context.disableJMX(); // Fake a component for test context.addComponent("amq", new MockComponent()); } @After public void tearDown() throws Exception { integration.stop(); } @Test public void testRoutes() throws Exception { // Adjust routes List<RouteDefinition> routes = context.getRouteDefinitions(); routes.get(0).adviceWith(context, new AdviceWithRouteBuilder() { @Override public void configure() throws Exception { // Replace "from" endpoint with direct:start replaceFromWith("direct:start"); // Mock and skip result endpoint mockEndpoints("log:*"); } }); MockEndpoint resultEndpoint = context.getEndpoint("mock:log:foo", MockEndpoint.class); // resultEndpoint.expectedMessageCount(1); // If you want to just check the number of messages resultEndpoint.expectedBodiesReceived("hello"); // If you want to check the contents // Start the integration integration.run(); // Send the test message context.createProducerTemplate().sendBody("direct:start", "hello"); resultEndpoint.assertIsSatisfied(); } }
Now, let's take a look at the interesting bits one by one.
Using property prefixing // Set property prefix for unit testing System.setProperty(CamelScrExample.PROPERTY_PREFIX, "unit"); This allows you to override parts of the configuration by prefixing properties with "unit.". For example, 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 integration.prepare(null, ScrHelper.getScrProperties(integration.getClass().getName())); Here we configure the Service Component in test with the same properties that would be used in OSGi environment.
Mocking components for test // Fake a component for test context.addComponent("amq", new MockComponent()); Components that are not available in test can be mocked like this to allow the route to start.
Adjusting routes for test // Adjust routes List<RouteDefinition> routes = context.getRouteDefinitions(); routes.get(0).adviceWith(context, new AdviceWithRouteBuilder() { @Override public void configure() throws Exception { // Replace "from" endpoint with direct:start replaceFromWith("direct:start"); // Mock and skip result endpoint mockEndpoints("log:*"); } }); Camel's AdviceWith feature allows routes to be modified for test.
Starting the routes // Start the integration integration.run(); Here we start the Service Component and along with it the routes.
Sending a test message // Send the test message context.createProducerTemplate().sendBody("direct:start", "hello"); Here we send a message to a route in test. Running the bundle in Apache KarafOnce the bundle has been built with Deploying the bundle in Apache Karaf # Add Camel feature repository karaf@root> features:chooseurl camel 2.15-SNAPSHOT # Install camel-scr feature karaf@root> features:install camel-scr # Install commons-lang, used in the example route to validate parameters karaf@root> osgi:install mvn:commons-lang/commons-lang/2.6 # Install and start your bundle karaf@root> osgi:install -s mvn:example/camel-scr-example/1.0-SNAPSHOT # See how it's running karaf@root> log:tail -n 10 Press ctrl-c to stop watching the log. Overriding the default configurationBy 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 Override a property # Override 'messageOk' property karaf@root> config:propset -p example.CamelScrExample messageOk "This is better logging" Or you can change the configuration by editing property files in Karaf's Using Camel SCR bundle as a templateLet'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 # Create a PID with a tail karaf@root> config:edit example.CamelScrExample-anotherone # Override some properties karaf@root> config:propset camelContextId my-other-context karaf@root> config:propset to "file://removeme?fileName=removemetoo.txt" # Save the PID karaf@root> config:update 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 |