Component docs
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/81f7515e Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/81f7515e Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/81f7515e Branch: refs/heads/master Commit: 81f7515e545f3ffde83d61a0b52629951f67c233 Parents: 26f1e95 Author: Claus Ibsen <davscl...@apache.org> Authored: Thu Apr 13 10:55:52 2017 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Thu Apr 13 10:55:52 2017 +0200 ---------------------------------------------------------------------- .../camel-scr/src/main/docs/camel-and-scr.adoc | 653 ------------------ components/camel-scr/src/main/docs/scr.adoc | 655 +++++++++++++++++++ 2 files changed, 655 insertions(+), 653 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/81f7515e/components/camel-scr/src/main/docs/camel-and-scr.adoc ---------------------------------------------------------------------- diff --git a/components/camel-scr/src/main/docs/camel-and-scr.adoc b/components/camel-scr/src/main/docs/camel-and-scr.adoc deleted file mode 100644 index b4cfe39..0000000 --- a/components/camel-scr/src/main/docs/camel-and-scr.adoc +++ /dev/null @@ -1,653 +0,0 @@ -## 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 - -INFO: *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* - -[source,java] ---------------------------------------------------------------------------------------------------------------- -@Component -@References({ - @Reference(name = "camelComponent",referenceInterface = ComponentResolver.class, - cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC, - policyOption = ReferencePolicyOption.GREEDY, bind = "gotCamelComponent", unbind = "lostCamelComponent") -}) ---------------------------------------------------------------------------------------------------------------- - -Then implement `getRouteBuilders()` method which returns the Camel -routes you want to run: - -*Implement getRouteBuilders()* - -[source,java] ------------------------------------------------------------------- - @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* - -[source,java] ---------------------------------------------------------- -@Properties({ - @Property(name = "camelContextId", value = "my-test"), - @Property(name = "active", value = "true"), - @Property(name = "...", value = "..."), - ... -}) ---------------------------------------------------------- - - - -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* - -[source,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; - } -} -------------------------------------------------------------------------------------------------------------------------------------------- - - - -`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* - -[source,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 `CamelScrExampleRoute` in more detail. - - - -[source,java] ----------------------------------------- - // 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. - - - -[source,java] ------------------------------------------------ - // 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. - - - -[source,java] ------------------------------------------------------------------------------------------------ - 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. - - - -[source,java] ----------------------------------------------------------------- - 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 - -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* - -[source,text] --------------------------------------------------------------------------------------------------------------- -$ 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: - -[source,java] ------------ -mvn install ------------ - -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`: - -[source,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* - -[source,java] --------------------------------------------------------------------- - // 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, `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* - -[source,java] ------------------------------------------------------------------------------------------------- - 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* - -[source,java] ---------------------------------------------------------- - // 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* - -[source,java] ------------------------------------------------------------------------- - // 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* - -[source,java] --------------------------------- - // Start the integration - integration.run(); --------------------------------- - -Here we start the Service Component and along with it the routes. - - - -*Sending a test message* - -[source,java] ---------------------------------------------------------------------------- - // 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 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* - -[source,text] ------------------------------------------------------------------------- -# 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 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* - -[source,text] ----------------------------------------------------------------------------------------- -# 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 -`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* - -[source,text] ------------------------------------------------------------------------- -# 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. - -### Notes - -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. http://git-wip-us.apache.org/repos/asf/camel/blob/81f7515e/components/camel-scr/src/main/docs/scr.adoc ---------------------------------------------------------------------- diff --git a/components/camel-scr/src/main/docs/scr.adoc b/components/camel-scr/src/main/docs/scr.adoc new file mode 100644 index 0000000..451af6a --- /dev/null +++ b/components/camel-scr/src/main/docs/scr.adoc @@ -0,0 +1,655 @@ +## Working with Camel and SCR + +**deprecated** + +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 + +INFO: *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* + +[source,java] +--------------------------------------------------------------------------------------------------------------- +@Component +@References({ + @Reference(name = "camelComponent",referenceInterface = ComponentResolver.class, + cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC, + policyOption = ReferencePolicyOption.GREEDY, bind = "gotCamelComponent", unbind = "lostCamelComponent") +}) +--------------------------------------------------------------------------------------------------------------- + +Then implement `getRouteBuilders()` method which returns the Camel +routes you want to run: + +*Implement getRouteBuilders()* + +[source,java] +------------------------------------------------------------------ + @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* + +[source,java] +--------------------------------------------------------- +@Properties({ + @Property(name = "camelContextId", value = "my-test"), + @Property(name = "active", value = "true"), + @Property(name = "...", value = "..."), + ... +}) +--------------------------------------------------------- + + + +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* + +[source,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; + } +} +------------------------------------------------------------------------------------------------------------------------------------------- + + + +`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* + +[source,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 `CamelScrExampleRoute` in more detail. + + + +[source,java] +---------------------------------------- + // 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. + + + +[source,java] +----------------------------------------------- + // 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. + + + +[source,java] +----------------------------------------------------------------------------------------------- + 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. + + + +[source,java] +---------------------------------------------------------------- + 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 + +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* + +[source,text] +-------------------------------------------------------------------------------------------------------------- +$ 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: + +[source,java] +----------- +mvn install +----------- + +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`: + +[source,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* + +[source,java] +-------------------------------------------------------------------- + // 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, `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* + +[source,java] +------------------------------------------------------------------------------------------------ + 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* + +[source,java] +--------------------------------------------------------- + // 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* + +[source,java] +------------------------------------------------------------------------ + // 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* + +[source,java] +-------------------------------- + // Start the integration + integration.run(); +-------------------------------- + +Here we start the Service Component and along with it the routes. + + + +*Sending a test message* + +[source,java] +--------------------------------------------------------------------------- + // 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 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* + +[source,text] +------------------------------------------------------------------------ +# 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 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* + +[source,text] +---------------------------------------------------------------------------------------- +# 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 +`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* + +[source,text] +------------------------------------------------------------------------ +# 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. + +### Notes + +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.