This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot-examples.git


The following commit(s) were added to refs/heads/main by this push:
     new df6f622  CAMEL-17154: Created Spring Boot example of the Dynamic 
Router EIP component. (#42)
df6f622 is described below

commit df6f6227f4bb6cc30b89eca4412c417aa1b31d04
Author: Steve Storck <steve...@gmail.com>
AuthorDate: Tue Jan 11 14:30:37 2022 -0500

    CAMEL-17154: Created Spring Boot example of the Dynamic Router EIP 
component. (#42)
---
 README.adoc                                        |   4 +-
 dynamic-router-eip/README.adoc                     | 117 +++++++++++
 dynamic-router-eip/pom.xml                         | 114 +++++++++++
 .../camel/example/springboot/Application.java      |  34 ++++
 .../config/DynamicRouterComponentConfig.java       | 226 +++++++++++++++++++++
 .../springboot/numbers/config/ExampleConfig.java   |  79 +++++++
 .../numbers/participants/PredicateConstants.java   | 110 ++++++++++
 .../numbers/participants/RoutingParticipant.java   | 177 ++++++++++++++++
 .../springboot/numbers/service/NumbersService.java | 129 ++++++++++++
 .../springboot/numbers/service/ResultsService.java |  96 +++++++++
 .../src/main/resources/application.yml             |  42 ++++
 pom.xml                                            |   1 +
 12 files changed, 1128 insertions(+), 1 deletion(-)

diff --git a/README.adoc b/README.adoc
index e5a7d98..975057c 100644
--- a/README.adoc
+++ b/README.adoc
@@ -27,7 +27,7 @@ readme's instructions.
 === Examples
 
 // examples: START
-Number of Examples: 54 (0 deprecated)
+Number of Examples: 55 (0 deprecated)
 
 [width="100%",cols="4,2,4",options="header"]
 |===
@@ -91,6 +91,8 @@ Number of Examples: 54 (0 deprecated)
         operations on a database
     
 
+| link:dynamic-router-eip/README.adoc[Dynamic Router Eip] (dynamic-router-eip) 
| EIP | An example on how to use the Dynamic Router EIP component in Spring Boot
+
 | link:hystrix/README.adoc[Hystrix] (hystrix) | EIP | An example showing how 
to use Hystrix EIP as circuit breaker in Camel routes
 
 | link:resilience4j/README.adoc[Resilience4j] (resilience4j) | EIP | An 
example showing how to use Resilience4j EIP as circuit breaker in Camel routes
diff --git a/dynamic-router-eip/README.adoc b/dynamic-router-eip/README.adoc
new file mode 100644
index 0000000..5da7f04
--- /dev/null
+++ b/dynamic-router-eip/README.adoc
@@ -0,0 +1,117 @@
+== Spring Boot Example with the Dynamic Router EIP Component
+
+=== Example Description
+
+This example demonstrates how you might use the Dynamic Router EIP component 
in a Spring Boot application.  There are eleven routing participants that 
subscribe to a "numbers" channel of the Dynamic Router.  The message includes:
+
+ 1. A Predicate that examines the Exchange to determine if an Exchange is 
appropriate for the participant
+ 2. A destination URI where the Dynamic Router will send Exchanges that match 
a participant's Predicate
+
+.Routing Participants
+[cols="1,1,2"]
+|===
+|Name |Priority |Description
+
+|Tens
+|1
+|Accepts multiples of 10.
+
+|Nines
+|2
+|Accepts multiples of 9.
+
+|Eights
+|3
+|Accepts multiples of 8.
+
+|Sevens
+|4
+|Accepts multiples of 7.
+
+|Sixes
+|5
+|Accepts multiples of 6.
+
+|Fives
+|6
+|Accepts multiples of 5.
+
+|Fours
+|7
+|Accepts multiples of 4.
+
+|Threes
+|8
+|Accepts multiples of 3.
+
+|Even
+|9
+|Accepts even numbers.
+
+|Odd
+|100
+|Accepts odd numbers.
+
+|Prime
+|10
+|Accepts prime numbers.
+|===
+
+Subscriptions with a lower priority value are evaluated earlier than 
subscriptions with a higher priority value.  For this reason, even though the 
number "10" is a multiple of 5, it will be consumed by the Tens participant.
+
+All participants inherit from the 
link:src/main/java/org/apache/camel/example/springboot/numbers/participants/RoutingParticipant.java[RoutingParticipant]
 class, so they use a `@Consume` annotation to consume from their registered 
destination URI.  They add their received number to the 
link:src/main/java/org/apache/camel/example/springboot/numbers/service/ResultsService.java[ResultsService].
+
+The 
link:src/main/java/org/apache/camel/example/springboot/numbers/service/NumbersService.java[NumbersService]
 sends all numbers from one to one million through the Dynamic Router.  After 
the messages have all been sent and processed, it instructs the ResultsService 
to print the results.  It will look something like this:
+
+.Example Output
+[source,bash]
+----
+  .   ____          _            __ _ _
+ /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
+( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
+ \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
+  '  |____| .__|_| |_|_| |_\__, | / / / /
+ =========|_|==============|___/=/_/_/_/
+ :: Spring Boot ::                (v2.6.2)
+
+INFO 23130 --- [main] o.a.c.e.s.n.service.NumbersService : Subscribing 
participants
+INFO 23130 --- [main] o.a.c.e.s.n.service.NumbersService : Sending messages to 
the dynamic router
+INFO 23130 --- [main] o.a.c.e.s.n.service.NumbersService : Finished
+Dynamic Router Spring Boot Numbers Example Results:
+            odd:  150077
+           even:  114287
+         sevens:  101588
+           tens:  100000
+          nines:  100000
+         eights:   88889
+         primes:   78494
+         threes:   76191
+          fives:   76190
+          sixes:   57142
+          fours:   57142
+Received count: 1000000 in 5709ms
+----
+
+=== Build
+
+You can build this example using:
+
+    $ mvn package
+
+=== Run
+
+You can run this example using:
+
+    $ mvn spring-boot:run
+
+The results should show up in less than ten seconds, and the program will 
immediately terminate.
+
+=== Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please
+https://camel.apache.org/support.html[let us know].
+
+We also love contributors, so please
+https://camel.apache.org/contributing.html[get involved]
+
+The Camel riders!
diff --git a/dynamic-router-eip/pom.xml b/dynamic-router-eip/pom.xml
new file mode 100644
index 0000000..170f763
--- /dev/null
+++ b/dynamic-router-eip/pom.xml
@@ -0,0 +1,114 @@
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.camel.springboot.example</groupId>
+        <artifactId>examples</artifactId>
+        <version>3.15.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-example-spring-boot-dynamic-router-eip</artifactId>
+    <name>Camel SB Examples :: Dynamic Router EIP</name>
+    <description>An example on how to use the Dynamic Router EIP component in 
Spring Boot</description>
+
+    <properties>
+        <category>EIP</category>
+        <spring.boot-version>${spring-boot-version}</spring.boot-version>
+    </properties>
+
+    <!-- Spring-Boot and Camel BOM -->
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring.boot-version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.camel.springboot</groupId>
+                <artifactId>camel-spring-boot-dependencies</artifactId>
+                <version>${project.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <!-- Camel -->
+        <dependency>
+            <groupId>org.apache.camel.springboot</groupId>
+            <artifactId>camel-dynamic-router-starter</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.springboot</groupId>
+            <artifactId>camel-direct-starter</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.springboot</groupId>
+            <artifactId>camel-bean-starter</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring.boot-version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.camel</groupId>
+                <artifactId>camel-package-maven-plugin</artifactId>
+                <version>${camel-version}</version>
+                <executions>
+                    <execution>
+                        <id>generate</id>
+                        <goals>
+                            <goal>generate-component</goal>
+                        </goals>
+                        <phase>process-classes</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
+
diff --git 
a/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/Application.java
 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/Application.java
new file mode 100644
index 0000000..64752fa
--- /dev/null
+++ 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/Application.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.springboot;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * We are so proud of our little Spring Boot Application.
+ */
+@SpringBootApplication(scanBasePackages = 
"org.apache.camel.example.springboot.numbers")
+public class Application {
+
+    /**
+     * Main method to start the application.  Please make us proud.
+     */
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}
diff --git 
a/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/config/DynamicRouterComponentConfig.java
 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/config/DynamicRouterComponentConfig.java
new file mode 100644
index 0000000..2747991
--- /dev/null
+++ 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/config/DynamicRouterComponentConfig.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.springboot.numbers.config;
+
+import org.apache.camel.ConsumerTemplate;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import 
org.apache.camel.example.springboot.numbers.participants.PredicateConstants;
+import 
org.apache.camel.example.springboot.numbers.participants.RoutingParticipant;
+import org.apache.camel.example.springboot.numbers.service.ResultsService;
+import 
org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static 
org.apache.camel.component.dynamicrouter.DynamicRouterConstants.COMPONENT_SCHEME;
+import static 
org.apache.camel.example.springboot.numbers.participants.PredicateConstants.PREDICATE_EIGHTS;
+import static 
org.apache.camel.example.springboot.numbers.participants.PredicateConstants.PREDICATE_EVEN;
+import static 
org.apache.camel.example.springboot.numbers.participants.PredicateConstants.PREDICATE_FIVES;
+import static 
org.apache.camel.example.springboot.numbers.participants.PredicateConstants.PREDICATE_FOURS;
+import static 
org.apache.camel.example.springboot.numbers.participants.PredicateConstants.PREDICATE_NINES;
+import static 
org.apache.camel.example.springboot.numbers.participants.PredicateConstants.PREDICATE_ODD;
+import static 
org.apache.camel.example.springboot.numbers.participants.PredicateConstants.PREDICATE_PRIMES;
+import static 
org.apache.camel.example.springboot.numbers.participants.PredicateConstants.PREDICATE_SEVENS;
+import static 
org.apache.camel.example.springboot.numbers.participants.PredicateConstants.PREDICATE_SIXES;
+import static 
org.apache.camel.example.springboot.numbers.participants.PredicateConstants.PREDICATE_TENS;
+import static 
org.apache.camel.example.springboot.numbers.participants.PredicateConstants.PREDICATE_THREES;
+
+/**
+ * This configuration ingests the config properties in the application.yml 
file.
+ * Sets up the Camel route that feeds the Dynamic Router.  Also creates all of 
the
+ * routing participants by using the various predicates defined in the
+ * {@link PredicateConstants} class.
+ */
+@Configuration
+@EnableConfigurationProperties(ExampleConfig.class)
+public class DynamicRouterComponentConfig {
+
+    /**
+     * Holds the config properties.
+     */
+    private final ExampleConfig exampleConfig;
+
+    /**
+     * Create this config with the config properties object.
+     *
+     * @param exampleConfig the config properties object
+     */
+    public DynamicRouterComponentConfig(final ExampleConfig exampleConfig) {
+        this.exampleConfig = exampleConfig;
+    }
+
+    /**
+     * Creates a simple route to allow a producer to send messages through
+     * the dynamic router.
+     */
+    @Bean
+    RouteBuilder myRouter() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from(exampleConfig.getStartUri()).to(COMPONENT_SCHEME + ":" + 
exampleConfig.getRoutingChannel());
+            }
+        };
+    }
+
+    /**
+     * Create a {@link RoutingParticipant} that handles messages where the 
body is comprised of a number that is
+     * a multiple of 10.
+     *
+     * @param subscriberTemplate the {@link ConsumerTemplate} for subscribing 
to messages that match
+     * @param resultsService     the service that handles the results of 
message processing
+     * @return the configured {@link RoutingParticipant}
+     */
+    @Bean
+    RoutingParticipant tensRoutingParticipant(final ProducerTemplate 
subscriberTemplate, final ResultsService resultsService) {
+        return new RoutingParticipant(exampleConfig, "tens", 1, 
PREDICATE_TENS, subscriberTemplate, resultsService);
+    }
+
+    /**
+     * Create a {@link RoutingParticipant} that handles messages where the 
body is comprised of a number that is
+     * a multiple of 9.
+     *
+     * @param subscriberTemplate the {@link ConsumerTemplate} for subscribing 
to messages that match
+     * @param resultsService     the service that handles the results of 
message processing
+     * @return the configured {@link RoutingParticipant}
+     */
+    @Bean
+    RoutingParticipant ninesRoutingParticipant(final ProducerTemplate 
subscriberTemplate, final ResultsService resultsService) {
+        return new RoutingParticipant(exampleConfig, "nines", 2, 
PREDICATE_NINES, subscriberTemplate, resultsService);
+    }
+
+    /**
+     * Create a {@link RoutingParticipant} that handles messages where the 
body is comprised of a number that is
+     * a multiple of 8.
+     *
+     * @param subscriberTemplate the {@link ConsumerTemplate} for subscribing 
to messages that match
+     * @param resultsService     the service that handles the results of 
message processing
+     * @return the configured {@link RoutingParticipant}
+     */
+    @Bean
+    RoutingParticipant eightsRoutingParticipant(
+            final ProducerTemplate subscriberTemplate, final ResultsService 
resultsService) {
+        return new RoutingParticipant(exampleConfig, "eights", 3, 
PREDICATE_EIGHTS, subscriberTemplate, resultsService);
+    }
+
+    /**
+     * Create a {@link RoutingParticipant} that handles messages where the 
body is comprised of a number that is
+     * a multiple of 7.
+     *
+     * @param subscriberTemplate the {@link ConsumerTemplate} for subscribing 
to messages that match
+     * @param resultsService     the service that handles the results of 
message processing
+     * @return the configured {@link RoutingParticipant}
+     */
+    @Bean
+    RoutingParticipant sevensRoutingParticipant(
+            final ProducerTemplate subscriberTemplate, final ResultsService 
resultsService) {
+        return new RoutingParticipant(exampleConfig, "sevens", 4, 
PREDICATE_SEVENS, subscriberTemplate, resultsService);
+    }
+
+    /**
+     * Create a {@link RoutingParticipant} that handles messages where the 
body is comprised of a number that is
+     * a multiple of 6.
+     *
+     * @param subscriberTemplate the {@link ConsumerTemplate} for subscribing 
to messages that match
+     * @param resultsService     the service that handles the results of 
message processing
+     * @return the configured {@link RoutingParticipant}
+     */
+    @Bean
+    RoutingParticipant sixesRoutingParticipant(final ProducerTemplate 
subscriberTemplate, final ResultsService resultsService) {
+        return new RoutingParticipant(exampleConfig, "sixes", 5, 
PREDICATE_SIXES, subscriberTemplate, resultsService);
+    }
+
+    /**
+     * Create a {@link RoutingParticipant} that handles messages where the 
body is comprised of a number that is
+     * a multiple of 5.
+     *
+     * @param subscriberTemplate the {@link ConsumerTemplate} for subscribing 
to messages that match
+     * @param resultsService     the service that handles the results of 
message processing
+     * @return the configured {@link RoutingParticipant}
+     */
+    @Bean
+    RoutingParticipant fivesRoutingParticipant(final ProducerTemplate 
subscriberTemplate, final ResultsService resultsService) {
+        return new RoutingParticipant(exampleConfig, "fives", 6, 
PREDICATE_FIVES, subscriberTemplate, resultsService);
+    }
+
+    /**
+     * Create a {@link RoutingParticipant} that handles messages where the 
body is comprised of a number that is
+     * a multiple of 4.
+     *
+     * @param subscriberTemplate the {@link ConsumerTemplate} for subscribing 
to messages that match
+     * @param resultsService     the service that handles the results of 
message processing
+     * @return the configured {@link RoutingParticipant}
+     */
+    @Bean
+    RoutingParticipant foursRoutingParticipant(final ProducerTemplate 
subscriberTemplate, final ResultsService resultsService) {
+        return new RoutingParticipant(exampleConfig, "fours", 7, 
PREDICATE_FOURS, subscriberTemplate, resultsService);
+    }
+
+    /**
+     * Create a {@link RoutingParticipant} that handles messages where the 
body is comprised of a number that is
+     * a multiple of 3.
+     *
+     * @param subscriberTemplate the {@link ConsumerTemplate} for subscribing 
to messages that match
+     * @param resultsService     the service that handles the results of 
message processing
+     * @return the configured {@link RoutingParticipant}
+     */
+    @Bean
+    RoutingParticipant threesRoutingParticipant(
+            final ProducerTemplate subscriberTemplate, final ResultsService 
resultsService) {
+        return new RoutingParticipant(exampleConfig, "threes", 8, 
PREDICATE_THREES, subscriberTemplate, resultsService);
+    }
+
+    /**
+     * Create a {@link RoutingParticipant} that handles messages where the 
body is comprised of a number that is
+     * an even number.
+     *
+     * @param subscriberTemplate the {@link ConsumerTemplate} for subscribing 
to messages that match
+     * @param resultsService     the service that handles the results of 
message processing
+     * @return the configured {@link RoutingParticipant}
+     */
+    @Bean
+    RoutingParticipant evensRoutingParticipant(final ProducerTemplate 
subscriberTemplate, final ResultsService resultsService) {
+        return new RoutingParticipant(exampleConfig, "even", 9, 
PREDICATE_EVEN, subscriberTemplate, resultsService);
+    }
+
+    /**
+     * Create a {@link RoutingParticipant} that handles messages where the 
body is comprised of a number that is
+     * an odd number.
+     *
+     * @param subscriberTemplate the {@link ConsumerTemplate} for subscribing 
to messages that match
+     * @param resultsService     the service that handles the results of 
message processing
+     * @return the configured {@link RoutingParticipant}
+     */
+    @Bean
+    RoutingParticipant oddsRoutingParticipant(final ProducerTemplate 
subscriberTemplate, final ResultsService resultsService) {
+        return new RoutingParticipant(exampleConfig, "odd", 100, 
PREDICATE_ODD, subscriberTemplate, resultsService);
+    }
+
+    /**
+     * Create a {@link RoutingParticipant} that handles messages where the 
body is comprised of a number that is
+     * a prime.
+     *
+     * @param subscriberTemplate the {@link ConsumerTemplate} for subscribing 
to messages that match
+     * @param resultsService     the service that handles the results of 
message processing
+     * @return the configured {@link RoutingParticipant}
+     */
+    @Bean
+    RoutingParticipant primesRoutingParticipant(
+            final ProducerTemplate subscriberTemplate, final ResultsService 
resultsService) {
+        return new RoutingParticipant(exampleConfig, "primes", 10, 
PREDICATE_PRIMES, subscriberTemplate, resultsService);
+    }
+}
diff --git 
a/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/config/ExampleConfig.java
 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/config/ExampleConfig.java
new file mode 100644
index 0000000..b6dfaa7
--- /dev/null
+++ 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/config/ExampleConfig.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.springboot.numbers.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * The config properties object from the application.yml file.
+ */
+@ConfigurationProperties(prefix = 
"camel.spring-boot.example.dynamic-router-eip")
+public class ExampleConfig {
+
+    /**
+     * The dynamic router channel.
+     */
+    private String routingChannel;
+
+    /**
+     * The dynamic router control channel URI
+     */
+    private String subscribeUri;
+
+    /**
+     * The scheme that routing participants will use to listen for messages.
+     */
+    private String receiverScheme;
+
+    /**
+     * The URI where messages will be sent to as the starting point for the
+     * route that feeds the dynamic router.
+     */
+    private String startUri;
+
+    public String getStartUri() {
+        return startUri;
+    }
+
+    public void setStartUri(String startUri) {
+        this.startUri = startUri;
+    }
+
+    public String getRoutingChannel() {
+        return routingChannel;
+    }
+
+    public void setRoutingChannel(String routingChannel) {
+        this.routingChannel = routingChannel;
+    }
+
+    public String getSubscribeUri() {
+        return subscribeUri;
+    }
+
+    public void setSubscribeUri(String subscribeUri) {
+        this.subscribeUri = subscribeUri;
+    }
+
+    public String getReceiverScheme() {
+        return receiverScheme;
+    }
+
+    public void setReceiverScheme(String receiverScheme) {
+        this.receiverScheme = receiverScheme;
+    }
+}
diff --git 
a/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/participants/PredicateConstants.java
 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/participants/PredicateConstants.java
new file mode 100644
index 0000000..7f441d6
--- /dev/null
+++ 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/participants/PredicateConstants.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.springboot.numbers.participants;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Predicate;
+
+import java.util.function.BiFunction;
+
+/**
+ * Provides various {@link Predicate}s that routing participants can send to 
the
+ * dynamic router as rules to determine exchange suitability.
+ */
+public abstract class PredicateConstants {
+
+    /**
+     * Gets the message body as an integer and determines if the number is 
evenly
+     * divided by the supplied integer.
+     */
+    public static final BiFunction<Exchange, Integer, Boolean> noRemainder = 
(e, m) ->
+            e.getIn().getBody(Integer.class) % m == 0;
+
+    /**
+     * Determines if the message body is a number that is even.
+     */
+    public static final Predicate PREDICATE_EVEN = e -> noRemainder.apply(e, 
2);
+
+    /**
+     * Determines if the message body is a number that is odd.
+     */
+    public static final Predicate PREDICATE_ODD = e -> 
e.getIn().getBody(Integer.class) % 2 != 0;
+
+    /**
+     * Determines if the message body is a number that is a multiple of 3.
+     */
+    public static final Predicate PREDICATE_THREES = e -> noRemainder.apply(e, 
3);
+
+    /**
+     * Determines if the message body is a number that is a multiple of 4.
+     */
+    public static final Predicate PREDICATE_FOURS = e -> noRemainder.apply(e, 
4);
+
+    /**
+     * Determines if the message body is a number that is a multiple of 5.
+     */
+    public static final Predicate PREDICATE_FIVES =  e -> noRemainder.apply(e, 
5);
+
+    /**
+     * Determines if the message body is a number that is a multiple of 6.
+     */
+    public static final Predicate PREDICATE_SIXES = e -> noRemainder.apply(e, 
6);
+
+    /**
+     * Determines if the message body is a number that is a multiple of 7.
+     */
+    public static final Predicate PREDICATE_SEVENS = e -> noRemainder.apply(e, 
7);
+
+    /**
+     * Determines if the message body is a number that is a multiple of 8.
+     */
+    public static final Predicate PREDICATE_EIGHTS = e -> noRemainder.apply(e, 
8);
+
+    /**
+     * Determines if the message body is a number that is a multiple of 9.
+     */
+    public static final Predicate PREDICATE_NINES = e -> noRemainder.apply(e, 
9);
+
+    /**
+     * Determines if the message body is a number that is a multiple of 10.
+     */
+    public static final Predicate PREDICATE_TENS = e -> noRemainder.apply(e, 
10);
+
+    /**
+     * If this predicate is prioritized with a higher number than {@link 
#PREDICATE_SEVENS}
+     * or {@link #PREDICATE_THREES} or {@link #PREDICATE_EVEN}, then this will 
miss 7, 3,
+     * and 2 in the accumulated list of prime numbers.
+     */
+    public static final Predicate PREDICATE_PRIMES = e -> {
+        int n = e.getIn().getBody(Integer.class);
+        // 2 is the first prime number
+        if (n <= 2) {
+            return n == 2;
+        }
+        // no other even numbers are prime
+        if (n % 2 == 0) {
+            return false;
+        }
+        // only some odd numbers might be prime
+        int max = (int) Math.sqrt(n) + 1;
+        for (int i = 3; i < max; i += 2) {
+            if (n % i == 0)
+                return false;
+        }
+        return true;
+    };
+}
diff --git 
a/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/participants/RoutingParticipant.java
 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/participants/RoutingParticipant.java
new file mode 100644
index 0000000..1790496
--- /dev/null
+++ 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/participants/RoutingParticipant.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.springboot.numbers.participants;
+
+import org.apache.camel.Consume;
+import org.apache.camel.Predicate;
+import org.apache.camel.ProducerTemplate;
+import 
org.apache.camel.component.dynamicrouter.message.DynamicRouterControlMessage;
+import 
org.apache.camel.component.dynamicrouter.message.DynamicRouterControlMessage.SubscribeMessageBuilder;
+import org.apache.camel.example.springboot.numbers.config.ExampleConfig;
+import org.apache.camel.example.springboot.numbers.service.ResultsService;
+
+/**
+ * A dynamic routing participant that knows how to build a control channel 
message
+ * for subscribing to dynamic router content.  A provided subscribe URI will 
allow
+ * an instance to consume messages sent to that URI.  Results are sent to the
+ * results service instance.
+ */
+public class RoutingParticipant {
+
+    /**
+     * Name of the dynamic router channel for an implementation to create a
+     * subscription for.
+     */
+    protected final String routingChannel;
+
+    /**
+     * The dynamic router control channel URI where subscribe messages will
+     * be sent.
+     */
+    protected final String subscribeUri;
+
+    /**
+     * For consuming messages that fit an implementation's rules, this is the
+     * scheme/component that the participant will listen on and consume from.
+     */
+    protected final String receiverScheme;
+
+    /**
+     * The "bin" or "category" of messages for a participant implementation.
+     * This value is also used along with the {@link #receiverScheme} to
+     * create the {@link #consumeUri}.
+     */
+    protected final String bin;
+
+    /**
+     * The priority of the processor when evaluated by the dynamic router.  
Lower
+     * number means higher priority.
+     */
+    protected final int priority;
+
+    /**
+     * The {@link Predicate} by which exchanges are evaluated for suitability 
for
+     * a routing participant.
+     */
+    protected final Predicate predicate;
+
+    /**
+     * The URI that a participant implementation will listen on for messages
+     * that match its rules.
+     */
+    protected final String consumeUri;
+
+    /**
+     * The {@link ProducerTemplate} to send subscriber messages to the dynamic
+     * router control channel.
+     */
+    protected final ProducerTemplate subscriberTemplate;
+
+    /**
+     * The service that accumulates results of dynamic routing from each
+     * participant.
+     */
+    protected final ResultsService resultsService;
+
+    public RoutingParticipant(
+            final ExampleConfig config,
+            final String bin,
+            final int priority,
+            final Predicate predicate,
+            final ProducerTemplate subscriberTemplate,
+            final ResultsService resultsService) {
+        this.bin = bin;
+        this.priority = priority;
+        this.predicate = predicate;
+        this.routingChannel = config.getRoutingChannel();
+        this.subscribeUri = config.getSubscribeUri();
+        this.receiverScheme = config.getReceiverScheme();
+        this.consumeUri = receiverScheme + ":" + bin;
+        this.subscriberTemplate = subscriberTemplate;
+        this.resultsService = resultsService;
+    }
+
+    /**
+     * This method consumes messages that have matched the participant's rules
+     * and have been routed to the participant.  It adds the results to the
+     * results service.
+     *
+     * @param number the numeric message body that is received
+     */
+    @Consume(property = "consumeUri")
+    public void consumeMessage(final int number) {
+        resultsService.addResult(bin, number);
+    }
+
+    /**
+     * Gets the consumer URI.
+     *
+     * @return the consumer URI
+     */
+    public String getConsumeUri() {
+        return this.consumeUri;
+    }
+
+    /**
+     * Sends the subscribe message to the dynamic router control channel.
+     */
+    public void subscribe() {
+        subscriberTemplate.sendBody(subscribeUri, createSubscribeMessage());
+    }
+
+    /**
+     * Gets the bin / category.
+     *
+     * @return the bin / category
+     */
+    protected String getBin() {
+        return bin;
+    }
+
+    /**
+     * Gets the implementation's rule priority.
+     *
+     * @return the rule priority
+     */
+    protected int getPriority() {
+        return priority;
+    }
+
+    /**
+     * The implementation's rule as a {@link Predicate}.
+     * @return the {@link Predicate} rule
+     */
+    protected Predicate getPredicate() {
+        return predicate;
+    }
+
+    /**
+     * Create a {@link DynamicRouterControlMessage} based on parameters from 
the
+     * implementing class.
+     *
+     * @return the {@link DynamicRouterControlMessage}
+     */
+    protected DynamicRouterControlMessage createSubscribeMessage() {
+        return new SubscribeMessageBuilder()
+                .id(getBin())
+                .channel(routingChannel)
+                .priority(getPriority())
+                .endpointUri(getConsumeUri())
+                .predicate(getPredicate())
+                .build();
+    }
+}
diff --git 
a/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/service/NumbersService.java
 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/service/NumbersService.java
new file mode 100644
index 0000000..80c8ad2
--- /dev/null
+++ 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/service/NumbersService.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.springboot.numbers.service;
+
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.example.springboot.numbers.config.ExampleConfig;
+import 
org.apache.camel.example.springboot.numbers.participants.RoutingParticipant;
+import org.apache.camel.util.StopWatch;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.IntStream;
+
+/**
+ * Create numbers and send them to the dynamic router so that they can be
+ * processed and binned to demonstrate the usage of the dynamic router
+ * component.
+ */
+@Service
+public class NumbersService {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(NumbersService.class);
+
+    /**
+     * The URI to send the messages to.  This URI feeds the dynamic router in a
+     * Camel route.
+     */
+    private final String startUri;
+
+    /**
+     * The routing participants that have subscribed to routing and have
+     * provided their rules for suitable exchanges.
+     */
+    private final List<RoutingParticipant> participants;
+
+    /**
+     * The {@link ProducerTemplate} to start the messages on the route.
+     */
+    @Produce(property = "startUri")
+    private final ProducerTemplate start;
+
+    /**
+     * The service that contains participants' results of processing the
+     * messages that they have received.
+     */
+    private final ResultsService resultsService;
+
+    /**
+     * The Spring Boot {@link ApplicationContext} so that the app can be
+     * programmatically shut down when we are finished processing all
+     * messages and displaying results.
+     */
+    private final ApplicationContext applicationContext;
+
+    /**
+     * Create this service with all of the things.
+     *
+     * @param config the configuration object representing the config 
properties
+     * @param participants the dynamic router participants
+     * @param start the producer template to send messages to the start 
endpoint
+     * @param resultsService the service that compiles routing results
+     * @param applicationContext the Spring app context for exiting when 
processing is complete
+     */
+    public NumbersService(
+            final ExampleConfig config,
+            final List<RoutingParticipant> participants,
+            final ProducerTemplate start,
+            final ResultsService resultsService,
+            final ApplicationContext applicationContext) {
+        this.startUri = config.getStartUri();
+        this.participants = participants;
+        this.start = start;
+        this.resultsService = resultsService;
+        this.applicationContext = applicationContext;
+    }
+
+    /**
+     * After the application is started and ready, tell each routing
+     * participant to subscribe, and then send the messages.  Afterward,
+     * display the results and exit the app.
+     */
+    @EventListener(ApplicationReadyEvent.class)
+    public void start() {
+        LOG.info("Subscribing participants");
+        participants.forEach(RoutingParticipant::subscribe);
+        StopWatch watch = new StopWatch();
+        LOG.info("Sending messages to the dynamic router");
+        sendMessages();
+        LOG.info(resultsService.getStatistics(watch));
+        SpringApplication.exit(applicationContext);
+    }
+
+    /**
+     * Sends the messages to the starting endpoint of the route.
+     */
+    public void sendMessages() {
+        IntStream.rangeClosed(1, 1000000).forEach(start::sendBody);
+    }
+
+    /**
+     * Gets the starting endpoint of the route.
+     *
+     * @return the starting endpoint of the route.
+     */
+    public String getStartUri() {
+        return startUri;
+    }
+}
diff --git 
a/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/service/ResultsService.java
 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/service/ResultsService.java
new file mode 100644
index 0000000..9265678
--- /dev/null
+++ 
b/dynamic-router-eip/src/main/java/org/apache/camel/example/springboot/numbers/service/ResultsService.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.springboot.numbers.service;
+
+import org.apache.camel.util.StopWatch;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+/**
+ * Holds processing results so that they can be displayed, etc.
+ */
+@Service
+public class ResultsService {
+
+    /**
+     * A counter to keep track of the number of results.
+     */
+    private final AtomicInteger count = new AtomicInteger(0);
+
+    /**
+     * A map of the "bin" or "category" to the list of values in that bin.
+     */
+    private final ConcurrentHashMap<String, List<Integer>> results;
+
+    /**
+     * Create the service and initialize the results map.
+     */
+    public ResultsService() {
+        results = new ConcurrentHashMap<>();
+    }
+
+    /**
+     * Add a result value for the given key/bin.
+     *
+     * @param key the bin or category
+     * @param value the value to add to the bin
+     */
+    public void addResult(final String key, int value) {
+        results.computeIfAbsent(key, v -> new ArrayList<>()).add(value);
+        count.incrementAndGet();
+    }
+
+    /**
+     * Get the results map.
+     *
+     * @return the results map
+     */
+    public Map<String, List<Integer>> getResults() {
+        return results;
+    }
+
+    /**
+     * Get the result count.
+     *
+     * @return the result count
+     */
+    public int getTotal() {
+        return count.get();
+    }
+
+    /**
+     * Get a message that contains the statistics of the messaging.
+     *
+     * @param watch a {@link StopWatch} that was started at the beginning of 
messaging
+     * @return a message that contains the statistics of the messaging
+     */
+    public String getStatistics(final StopWatch watch) {
+        return results.entrySet().stream()
+                .sorted((o1, o2) -> o2.getValue().size() - 
o1.getValue().size())
+                .map(e -> String.format("%7s: %7d", e.getKey(), 
e.getValue().size()))
+                .collect(Collectors.joining("\n\t",
+                        "Finished\nDynamic Router Spring Boot Numbers Example 
Results:\n\t",
+                        String.format("\nReceived count: %d in %dms", 
getTotal(), watch.taken())));
+
+    }
+}
diff --git a/dynamic-router-eip/src/main/resources/application.yml 
b/dynamic-router-eip/src/main/resources/application.yml
new file mode 100644
index 0000000..a0d6518
--- /dev/null
+++ b/dynamic-router-eip/src/main/resources/application.yml
@@ -0,0 +1,42 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+camel:
+  springboot:
+    name: CamelSpringBootDynamicRouterExample
+    jmx-enabled: false
+    shutdown-timeout: 30
+  spring-boot:
+    example:
+      dynamic-router-eip:
+        routing-channel: numbers
+        subscribe-uri: dynamic-router:control
+        receiver-scheme: direct
+        start-uri: direct:start
+  component:
+    dynamic-router:
+      lazy-start-producer: true
+      bridge-error-handler: true
+  cloud:
+    enabled: false
+logging:
+  level:
+    org.springframework: WARN
+    org.apache.camel: WARN
+    org.apache.camel.example.springboot.numbers: INFO
+spring:
+  profiles:
+    active: default
diff --git a/pom.xml b/pom.xml
index 36eb9fa..e0911a3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,6 +43,7 @@
                <module>arangodb</module>
                <module>aws2-s3</module>
                <module>clustered-route-controller</module>
+               <module>dynamic-router-eip</module>
                <module>fhir</module>
                <module>fhir-auth-tx</module>
                <module>geocoder</module>

Reply via email to