= Dynamic Router Component
:doctitle: Dynamic Router
:shortname: dynamic-router
:artifactid: camel-dynamic-router
:description: The Dynamic Router component routes exchanges to recipients, and the recipients (and their rules) may change at runtime.
:since: 3.15
:supportlevel: Stable
:tabs-sync-option:
:component-header: Only producer is supported
:core:
//Manually maintained attributes
:camel-spring-boot-name: dynamic-router

*Since Camel {since}*

*{component-header}*

The Dynamic Router Component is an implementation of the Dynamic Router EIP.  Participants may send subscription
messages over a special control channel, at runtime, to specify the conditions under which messages are routed to their
endpoint (also provided in the control channel message).  In this way, the Dynamic Router is an extension of the
content-based router EIP.  When a recipient wishes to remove itself, it can also send a message to unsubscribe.

Note that, while Camel Core contains an implementation of the Dynamic Router EIP, this component is a completely
separate implementation that aims to be a closer reflection of the EIP description.  The main differences between the
Core implementation and this component implementation are as follows:

_Control Channel_:: A reserved communication channel by which routing participants can subscribe or unsubscribe to
receiving messages that meet their criteria.
 * *core*: does not have a communication channel for control messages.  Perhaps the "re-circulation" behavior, discussed
below, is the core Dynamic Router's control channel interpretation.
 * *component*: provides a control channel for participants to subscribe and unsubscribe with control messages that
contain a `Predicate` to determine `Exchange` suitability, and the `Endpoint` URI that a matching `Exchange` will be
sent to.
 _Dynamic Rule Base_:: The Dynamic Router should have a list of routing recipients' criteria that define the terms under
which an exchange is suitable for them to receive.
 * *core*: implements a dynamic version of a `Routing Slip` for this purpose, but that is not inherently dynamic in
terms of its content. If the content of this slip is dynamic, it will be up to the user to define and implement
that capability.
 * *component*: builds the rule base at runtime, and maintains it as participants subscribe or unsubscribe via the
control channel.
 _Message Re-Circulation_:: The Dynamic Router EIP description does not specify any message re-circulation behavior.
 * *core*: provides a feature that continuously routes the exchange to a recipient, then back through the dynamic
router, until a recipient returns `null` to signify routing termination.  This may be an interpretation of the control
channel feature.
 * *component*: does not provide a re-circulation feature.  If this is the desired behavior, the user will have to
define and implement this behavior.  E.g., create a simple route to send a response back through the Dynamic Router
under some condition(s).

For some use cases, the core Dynamic Router will be more appropriate.  In other cases, the Dynamic Router Component will
be a better fit.

== URI format

----
dynamic-router:channel[?options]
----

The `channel` is the routing channel that allows messaging to be logically separate from other channels.  Any string that can be included in a URI is a valid channel name.  Each channel can have a set of participant subscriptions, and can consume messages to be routed to appropriate recipients.  The only reserved channel is the `control` channel.  This is a single channel that handles control messages for participants to subscribe or unsubscribe for messaging over a desired channel.

These messages will be described in greater detail below, with examples.

// component-configure options: START
// component-configure options: END

// component options: START
include::partial$component-configure-options.adoc[]
include::partial$component-endpoint-options.adoc[]
// component options: END

// endpoint options: START
// endpoint options: END

== Usage

Maven users will need to add the following dependency to their `pom.xml` for this component:

[source,xml]
----
<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-dynamic-router</artifactId>
    <version>x.x.x</version>
    <!-- use the same version as your Camel core version -->
</dependency>
----

Gradle users will need to add the following dependency to their `build.gradle` for this component:

[source,groovy]
----
implementation group: 'org.apache.camel', name: 'camel-dynamic-router', version: 'x.x.x'
// use the same version as your Camel core version
----

The Dynamic Router component is used in the same way that other components are used. Include the dynamic-router
URI as a consumer in a route, along with the channel name.


[tabs]
====
Java::
+
.Example Java DSL Route Definition
[source,java]
----
// Send a message to the Dynamic Router channel named "orders"
from("direct:start").to("dynamic-router:orders");
----

Spring XML::
+
.Example XML Route Definition
[source,xml]
----
<route>
   <from uri="direct:start"/>
   <to uri="dynamic-router:nameOfYourChannel"/>
</route>
----
====

== Examples

The benefit of the Dynamic Router EIP Component can best be seen, perhaps, through looking at some use cases.  These
examples are not the only possibilities with this component, but they show the basics of two main usages -- message
routing within a single JVM, and message routing across multiple JVMs.

=== Dynamic Router within a single JVM or Application

The Dynamic Router EIP component can receive messages from a single source and dispatch them to interested recipients.
If we have a simple point-of-sale application, we might have services that:

 1. Process orders
 2. Adjust inventory counts
 3. Process returns

For the purpose of this example, the exact steps that each service carries out are not as important as the fact that
each service needs to be notified that it needs to do something under the right condition(s).  So, each service will
subscribe to participate in routing:

.Orders processing service subscription
[source,java]
----
DynamicRouterControlMessage controlMessage = DynamicRouterControlMessage.newBuilder()
    .subscribeChannel("orders")
    .subscriptionId("orderProcessing")
    .destinationUri("direct:orders")
    .priority(5)
    .predicate("{(headers.command == 'processOrder'}")
    .expressionLanguage("simple")
    .build();
producerTemplate.sendBody("dynamic-router-control:subscribe", controlMessage);
----

.Inventory service subscription
[source,java]
----
DynamicRouterControlMessage controlMessage = DynamicRouterControlMessage.newBuilder()
    .subscribeChannel("orders")
    .subscriptionId("inventoryProcessing")
    .destinationUri("direct:orders")
    .priority(5)
    .predicate("{headers.command == 'processOrder' or headers.command == 'processReturn'}")
    .expressionLanguage("simple")
    .build();
producerTemplate.sendBody("dynamic-router-control:subscribe", controlMessage);
----

.Returns processing service subscription
[source,java]
----
DynamicRouterControlMessage controlMessage = DynamicRouterControlMessage.newBuilder()
    .subscribeChannel("orders")
    .subscriptionId("orderProcessing")
    .destinationUri("direct:orders")
    .priority(5)
    .predicate("{(headers.command == 'processReturn'}")
    .expressionLanguage("simple")
    .build();
producerTemplate.sendBody("dynamic-router-control:subscribe", controlMessage);
----

Above, we have the Orders service subscribing for all messages where the `command` header is "processOrder", and the
Returns service subscribing for all messages where the `command` header is "processReturn".  The Inventory service is
interested in *both* types of messages, since it must deduct from the inventory when an order request comes through,
and it must add to inventory counts when a return request comes through.  So, for either type of message, two services
will be notified.

The order messages get sent to the dynamic router:

.Routing order/return request messages
[source,java]
----
from("direct:start")
    .process(myOrderProcessor)
    .to("dynamic-router:orders");
----

Note the `.process(myOrderProcessor)` step.  If incoming messages need to be validated, enriched, transformed, or
otherwise augmented, that can be done before the Dynamic Router receives the message.  Then, when the Dynamic Router
receives a message, it checks the `Exchange` against all subscriptions for the _orders_ channel to determine if it is
suitable for any of the recipients.  Orders should have a header (`command` -> `processOrder`), so the message will be
routed to the _orders_ service, and the inventory service.  The system will process the order details, and then the
inventory service will deduct from merchandize counts.  Likewise, returns should have a header
(`command` -> `processReturn`), so the message will be routed to the returns service, where the return details will be
processed, and the inventory service will increase the relevant merchandise counts.

==== Further learning: a complete Spring Boot example

In the `camel-spring-boot-examples` project, the `dynamic-router-eip` module serves as a complete example in this
category that you can run and/or experiment with to get a practical feel for how you might use this in your own
single-JVM application.

=== Dynamic Router across multiple JVMs or Applications

The Dynamic Router EIP component is particularly well-suited to serve as the primary orchestration mechanism between
various applications and services that comprise an application stack.  Note that the Dynamic Router cannot achieve this
by itself, and that some other transport is required to allow messages to pass between services that exist in separate
JVMs.  For example, a message transport implementation like Kafka, Artemis, or Protocol Buffers, could be used.

Let's look at the point-of-sale example in a different context.  In a microservice architecture, this system would have
several separate application modules, with the orders service, inventory service, and returns service, contained within
their own microservice (application).  Similar to the single-JVM example, all services will subscribe, but they will
need to send their subscriptions through a transport that can communicate to another JVM.  Their subscriptions might
look like:

.Orders processing service subscription
[source,java]
----
DynamicRouterControlMessage controlMessage = DynamicRouterControlMessage.newBuilder()
    .subscribeChannel("orders")
    .subscriptionId("orderProcessing")
    .destinationUri("direct:orders")
    .priority(5)
    .predicate("{(headers.command == 'processOrder'}")
    .expressionLanguage("simple")
    .build();
ObjectMapper mapper = new ObjectMapper(new JsonFactory());
producerTemplate.sendBody("kafka://subscriptions", mapper.writeValueAsString(controlMessage));
----

.Inventory service subscription
[source,java]
----
DynamicRouterControlMessage controlMessage = DynamicRouterControlMessage.newBuilder()
    .subscribeChannel("orders")
    .subscriptionId("inventoryProcessing")
    .destinationUri("direct:orders")
    .priority(5)
    .predicate("{headers.command == 'processOrder' or headers.command == 'processReturn'}")
    .expressionLanguage("simple")
    .build();
ObjectMapper mapper = new ObjectMapper(new JsonFactory());
producerTemplate.sendBody("kafka://subscriptions", mapper.writeValueAsString(controlMessage));
----

.Returns processing service subscription
[source,java]
----
DynamicRouterControlMessage controlMessage = DynamicRouterControlMessage.newBuilder()
    .subscribeChannel("orders")
    .subscriptionId("orderProcessing")
    .destinationUri("direct:orders")
    .priority(5)
    .predicate("{(headers.command == 'processReturn'}")
    .expressionLanguage("simple")
    .build();
ObjectMapper mapper = new ObjectMapper(new JsonFactory());
producerTemplate.sendBody("kafka://subscriptions", mapper.writeValueAsString(controlMessage));
----

In another module, additional routing will serve as a bridge to get the message from Kafka to the control channel of the
Dynamic Router:

.Bridge from Kafka to the Dynamic Router control channel
[source,java]
----
RouteBuilder subscriptionRouter() {
    return new RouteBuilder(camelContext) {
        @Override
        public void configure() {
            from("kafka:subscriptions")
                .unmarshal().json(DynamicRouterControlMessage.class)
                .to("dynamic-router-control:subscribe");
        }
    };
}
----

Order requests or return requests might also arrive via Kafka.  The route is essentially the same as the route in the
single-JVM example.  Instead of forwarding the incoming message, as-is, from the "direct" component to the router,
the messages are deserialized from a String, and converted to an instance of the "order" object.  Then, it can be sent
to the Dynamic Router for evaluation and distribution to the appropriate subscribing recipients:

.Routing order/return request messages from Kafka to the Dynamic Router
[source,java]
----
from("kafka://orders")
    .unmarshal().json(MyOrderMessage.class)
    .process(myOrderProcessor)
    .to("dynamic-router:orders");
----

Note the `.process(myOrderProcessor)` step.  If incoming messages need to be validated, enriched, transformed, or
otherwise augmented, that can be done before the Dynamic Router receives the message.  Then, when the Dynamic Router
receives a message, it checks the `Exchange` against all subscriptions for the "orders" channel to determine if it is
suitable for any of the recipients.  Orders should have a header (`command` -> `processOrder`), so the message will be
routed to the orders service, and the inventory service.  The system will process the order details, and then the
inventory service will deduct from merchandise counts.  Likewise, returns should have a header
(`command` -> `processReturn`), so the message will be routed to the returns service, where the return details will be
processed, and the inventory service will increase the relevant merchandise counts.

==== Further learning: a complete Spring Boot example

In the `camel-spring-boot-examples` project, the `dynamic-router-eip-multimodule` module serves as a complete example
in this category that you can run and/or experiment with to get a practical feel for how you might use this in your own
multi-JVM application stack.

=== JMX Control and Monitoring Operations

The Dynamic Router Control component supports some JMX operations that allow you to control and monitor the component.
It is beyond the scope of this document to go into detail about JMX, so this is a list of the operations that are
supported.  For more information about JMX, see the xref:manual::jmx.adoc[JMX] documentation.

.Subscribing with a predicate expression
[source,java]
----
String subscribeWithPredicateExpression(String, String, String, int, String, String, boolean)
----
This operation provides the ability to subscribe to a channel with a predicate expression. The parameters, in order, are
as follows:

- subscription ID
- channel name
- destination URI
- priority
- predicate expression
- expression language
- update the subscription (true), or add a new one (false)

.Subscribing with a predicate bean
[source,java]
----
String subscribeWithPredicateBean(String, String, String, int, String, boolean)
----
This operation provides the ability to subscribe to a channel with the name of a Predicate that has been bound in the
registry. The parameters, in order, are as follows:

- subscription ID
- channel name
- destination URI
- priority
- predicate bean name
- update the subscription (true), or add a new one (false)

.Subscribing with a predicate instance
[source,java]
----
String subscribeWithPredicateInstance(String, String, String, int, Object, boolean)
----
This operation provides the ability to subscribe to a channel with an instance of a Predicate. The parameters, in order,
are as follows:

- subscription ID
- channel name
- destination URI
- priority
- predicate instance
- update the subscription (true), or add a new one (false)

.Unsubscribing
[source,java]
----
boolean removeSubscription(String, String)
----
This operation provides the ability to unsubscribe from a channel. The parameters, in order, are as follows:

- subscription ID
- channel name

.Getting the subscriptions map
[source,java]
----
Map<String, ConcurrentSkipListSet<PrioritizedFilter>> getSubscriptionsMap()
----
This operation provides the ability to get the subscriptions map.  The map is keyed by channel name, and the values are
a set of prioritized filters.

.Getting the subscriptions statistics map
[source,java]
----
Map<String, List<PrioritizedFilterStatistics>> getSubscriptionsStatisticsMap()
----
This operation provides the ability to get the subscriptions statistics map.  The map is keyed by channel name, and the
values are a list of prioritized filter statistics, including the number of messages that have matched the filter, and
had the exchange sent to the destination URI.
