use std::any::Any;
use std::cmp::Ordering;
use std::sync::Arc;
use std::time::Duration;
use crate::builder::{constant, simple, ExpressionDefinition, IntoExpression};
use crate::error::CamelError;
use crate::model::{ProcessorDefinition, RouteDefinition};
use crate::processor::routing::aggregate::{AggregateDefinition, AggregationRepository};
use crate::processor::routing::choice::ChoiceDefinition;
use crate::processor::routing::claim_check::{ClaimCheckDefinition, ClaimCheckOperation};
use crate::processor::routing::enrich::{EnrichDefinition, PollEnrichDefinition};
use crate::processor::routing::filter::{FilterDefinition, PipelineDefinition};
use crate::processor::routing::load_balance::{FailoverSettings, LoadBalanceDefinition, LoadBalancer, LoadBalancerPolicy};
use crate::processor::routing::loop_eip::LoopDefinition;
use crate::processor::routing::multicast::MulticastDefinition;
use crate::processor::routing::options::{Deferred, EipOption, EipOptions, OptionTarget};
use crate::processor::routing::recipient_list::{DynamicRouterDefinition, RecipientListDefinition, RoutingSlipDefinition};
use crate::processor::routing::resequence::{ResequenceDefinition, ResequencerConfig};
use crate::processor::routing::sort::SortDefinition;
use crate::processor::routing::split::SplitDefinition;
use crate::processor::routing::support::StrategyDefinition;
use crate::processor::routing::wire_tap::WireTapDefinition;
use crate::spi::{AggregationStrategy, Processor};
use crate::value::Value;
const LAST_NODE: &str = "camel.routing.lastNode";
struct LastNode {
depth: usize,
count: usize,
target: Arc<dyn OptionTarget>,
}
fn deep_count(d: &dyn ProcessorDefinition) -> usize {
d.outputs().into_iter().map(|c| 1 + deep_count(c)).sum()
}
fn block_options(any: &mut dyn Any) -> Option<&mut dyn EipOptions> {
if any.is::<SplitDefinition>() {
return any.downcast_mut::<SplitDefinition>().map(|d| d as &mut dyn EipOptions);
}
if any.is::<MulticastDefinition>() {
return any.downcast_mut::<MulticastDefinition>().map(|d| d as &mut dyn EipOptions);
}
if any.is::<AggregateDefinition>() {
return any.downcast_mut::<AggregateDefinition>().map(|d| d as &mut dyn EipOptions);
}
if any.is::<LoopDefinition>() {
return any.downcast_mut::<LoopDefinition>().map(|d| d as &mut dyn EipOptions);
}
if any.is::<ResequenceDefinition>() {
return any.downcast_mut::<ResequenceDefinition>().map(|d| d as &mut dyn EipOptions);
}
None
}
fn uri_expression(uri: String) -> ExpressionDefinition {
if uri.contains("${") {
simple(uri).def
} else {
constant(uri).def
}
}
impl RouteDefinition {
fn routing_container_count(&self) -> usize {
match self.stack.last() {
Some(b) => deep_count(b.as_ref()) + self.pending.as_ref().map(|p| 1 + deep_count(p.as_ref())).unwrap_or(0),
None => self.outputs.iter().map(|o| 1 + deep_count(o.as_ref())).sum(),
}
}
fn add_deferred<T: ProcessorDefinition + EipOptions>(&mut self, def: T) -> &mut Self {
let d = Deferred::new(def);
let target: Arc<dyn OptionTarget> = d.inner.clone();
self.add(Box::new(d));
let rec = LastNode { depth: self.stack.len(), count: self.routing_container_count(), target };
self.extensions.insert(LAST_NODE.to_string(), Box::new(rec));
self
}
fn last_routing_node(&self) -> Option<Arc<dyn OptionTarget>> {
let rec = self.extension::<LastNode>(LAST_NODE)?;
if rec.depth == self.stack.len() && rec.count == self.routing_container_count() {
Some(rec.target.clone())
} else {
None
}
}
pub fn routing_option(&mut self, option: EipOption) -> &mut Self {
if let Some(t) = self.last_routing_node() {
if t.apply(&option) {
return self;
}
}
self.flush_pending();
if let Some(top) = self.stack.last_mut() {
if let Some(o) = block_options(top.as_any_mut()) {
if o.apply_option(&option) {
return self;
}
}
}
let msg = format!("Option {} is not supported at this position in the route", option.name());
self.add_build_error(CamelError::illegal_argument(msg));
self
}
fn with_last_node<T: 'static>(&mut self, option: &str, f: impl FnOnce(&mut T)) -> &mut Self {
let mut f = Some(f);
let mut applied = false;
if let Some(t) = self.last_routing_node() {
t.with_any(&mut |a: &mut dyn Any| {
if let Some(d) = a.downcast_mut::<T>() {
if let Some(f) = f.take() {
f(d);
applied = true;
}
}
});
}
if !applied {
self.add_build_error(CamelError::illegal_argument(format!("Option {} is not supported at this position in the route", option)));
}
self
}
fn with_block<T: 'static>(&mut self, option: &str, f: impl FnOnce(&mut T)) -> &mut Self {
match self.current_block_mut::<T>() {
Some(b) => f(b),
None => {
let msg = format!("Option {} must be used directly after its EIP", option);
self.add_build_error(CamelError::illegal_argument(msg));
}
}
self
}
pub fn parallel_processing(&mut self) -> &mut Self {
self.routing_option(EipOption::ParallelProcessing(true))
}
pub fn streaming(&mut self) -> &mut Self {
self.routing_option(EipOption::Streaming(true))
}
pub fn stop_on_exception(&mut self) -> &mut Self {
self.routing_option(EipOption::StopOnException(true))
}
pub fn share_unit_of_work(&mut self) -> &mut Self {
self.routing_option(EipOption::ShareUnitOfWork(true))
}
pub fn timeout(&mut self, d: Duration) -> &mut Self {
self.routing_option(EipOption::Timeout(d))
}
pub fn aggregation_strategy(&mut self, s: impl AggregationStrategy + 'static) -> &mut Self {
self.routing_option(EipOption::AggregationStrategy(StrategyDefinition::instance(s)))
}
pub fn aggregation_strategy_arc(&mut self, s: Arc<dyn AggregationStrategy>) -> &mut Self {
self.routing_option(EipOption::AggregationStrategy(StrategyDefinition::Instance(s)))
}
pub fn aggregation_strategy_ref(&mut self, name: impl Into<String>) -> &mut Self {
self.routing_option(EipOption::AggregationStrategy(StrategyDefinition::Ref(name.into())))
}
pub fn delimiter(&mut self, d: impl Into<String>) -> &mut Self {
self.routing_option(EipOption::Delimiter(d.into()))
}
pub fn ignore_invalid_endpoints(&mut self) -> &mut Self {
self.routing_option(EipOption::IgnoreInvalidEndpoints(true))
}
pub fn copy(&mut self, copy: bool) -> &mut Self {
self.routing_option(EipOption::Copy(copy))
}
pub fn aggregate_on_exception(&mut self) -> &mut Self {
self.routing_option(EipOption::AggregateOnException(true))
}
pub fn executor_pool_size(&mut self, n: usize) -> &mut Self {
self.routing_option(EipOption::PoolSize(n))
}
pub fn filter(&mut self, predicate: impl IntoExpression) -> &mut Self {
self.push_block(Box::new(FilterDefinition::new(predicate.into_expression())))
}
pub fn choice(&mut self) -> &mut Self {
self.push_block(Box::new(ChoiceDefinition::new()))
}
pub fn when(&mut self, predicate: impl IntoExpression) -> &mut Self {
let r = match self.unwind_to_block::<ChoiceDefinition>() {
Some(c) => c.add_when(predicate.into_expression()),
None => Err(CamelError::illegal_argument("when() must be used inside a choice()")),
};
if let Err(e) = r {
self.add_build_error(e);
}
self
}
pub fn otherwise(&mut self) -> &mut Self {
let r = match self.unwind_to_block::<ChoiceDefinition>() {
Some(c) => c.add_otherwise(),
None => Err(CamelError::illegal_argument("otherwise() must be used inside a choice()")),
};
if let Err(e) = r {
self.add_build_error(e);
}
self
}
pub fn end_choice(&mut self) -> &mut Self {
if self.unwind_to_block::<ChoiceDefinition>().is_none() {
self.add_build_error(CamelError::illegal_argument("endChoice() must be used inside a choice()"));
}
self
}
pub fn precondition(&mut self) -> &mut Self {
let ok = match self.unwind_to_block::<ChoiceDefinition>() {
Some(c) => {
c.precondition = true;
true
}
None => false,
};
if !ok {
self.add_build_error(CamelError::illegal_argument("precondition() must be used on a choice()"));
}
self
}
pub fn pipeline(&mut self) -> &mut Self {
self.push_block(Box::new(PipelineDefinition::default()))
}
pub fn split(&mut self, expression: impl IntoExpression) -> &mut Self {
self.push_block(Box::new(SplitDefinition::new(expression.into_expression())))
}
pub fn multicast(&mut self) -> &mut Self {
self.push_block(Box::new(MulticastDefinition::new()))
}
pub fn recipient_list(&mut self, expression: impl IntoExpression) -> &mut Self {
self.add_deferred(RecipientListDefinition::new(expression.into_expression()))
}
pub fn routing_slip(&mut self, expression: impl IntoExpression) -> &mut Self {
self.add_deferred(RoutingSlipDefinition::new(expression.into_expression()))
}
pub fn dynamic_router(&mut self, expression: impl IntoExpression) -> &mut Self {
self.add_deferred(DynamicRouterDefinition::new(expression.into_expression()))
}
pub fn aggregate(&mut self, correlation: impl IntoExpression, strategy: impl AggregationStrategy + 'static) -> &mut Self {
self.push_block(Box::new(AggregateDefinition::new(correlation.into_expression(), Some(StrategyDefinition::instance(strategy)))))
}
pub fn aggregate_ref(&mut self, correlation: impl IntoExpression, strategy_ref: impl Into<String>) -> &mut Self {
self.push_block(Box::new(AggregateDefinition::new(correlation.into_expression(), Some(StrategyDefinition::Ref(strategy_ref.into())))))
}
pub fn aggregate_correlation(&mut self, correlation: impl IntoExpression) -> &mut Self {
self.push_block(Box::new(AggregateDefinition::new(correlation.into_expression(), None)))
}
pub fn completion_size(&mut self, n: usize) -> &mut Self {
self.with_block::<AggregateDefinition>("completionSize", |a| a.completion_size = Some(n))
}
pub fn completion_size_expr(&mut self, e: impl IntoExpression) -> &mut Self {
let e = e.into_expression();
self.with_block::<AggregateDefinition>("completionSize", |a| a.completion_size_expression = Some(e))
}
pub fn completion_predicate(&mut self, p: impl IntoExpression) -> &mut Self {
let p = p.into_expression();
self.with_block::<AggregateDefinition>("completionPredicate", |a| a.completion_predicate = Some(p))
}
pub fn completion_timeout(&mut self, d: Duration) -> &mut Self {
self.with_block::<AggregateDefinition>("completionTimeout", |a| a.completion_timeout = Some(d))
}
pub fn completion_timeout_expr(&mut self, e: impl IntoExpression) -> &mut Self {
let e = e.into_expression();
self.with_block::<AggregateDefinition>("completionTimeout", |a| a.completion_timeout_expression = Some(e))
}
pub fn completion_interval(&mut self, d: Duration) -> &mut Self {
self.with_block::<AggregateDefinition>("completionInterval", |a| a.completion_interval = Some(d))
}
pub fn completion_from_batch_consumer(&mut self) -> &mut Self {
self.with_block::<AggregateDefinition>("completionFromBatchConsumer", |a| a.completion_from_batch_consumer = true)
}
pub fn completion_on_new_correlation_group(&mut self) -> &mut Self {
self.with_block::<AggregateDefinition>("completionOnNewCorrelationGroup", |a| a.completion_on_new_correlation_group = true)
}
pub fn eager_check_completion(&mut self) -> &mut Self {
self.with_block::<AggregateDefinition>("eagerCheckCompletion", |a| a.eager_check_completion = true)
}
pub fn force_completion_on_stop(&mut self) -> &mut Self {
self.with_block::<AggregateDefinition>("forceCompletionOnStop", |a| a.force_completion_on_stop = true)
}
pub fn discard_on_completion_timeout(&mut self) -> &mut Self {
self.with_block::<AggregateDefinition>("discardOnCompletionTimeout", |a| a.discard_on_completion_timeout = true)
}
pub fn ignore_invalid_correlation_keys(&mut self) -> &mut Self {
self.with_block::<AggregateDefinition>("ignoreInvalidCorrelationKeys", |a| a.ignore_invalid_correlation_keys = true)
}
pub fn close_correlation_key_on_completion(&mut self, max_keys: usize) -> &mut Self {
self.with_block::<AggregateDefinition>("closeCorrelationKeyOnCompletion", |a| a.close_correlation_key_on_completion = Some(max_keys))
}
pub fn aggregation_repository(&mut self, repo: Arc<dyn AggregationRepository>) -> &mut Self {
self.with_block::<AggregateDefinition>("aggregationRepository", |a| a.repository = Some(repo))
}
pub fn wire_tap(&mut self, uri: impl Into<String>) -> &mut Self {
self.add_deferred(WireTapDefinition::new(uri))
}
pub fn new_exchange_body(&mut self, e: impl IntoExpression) -> &mut Self {
let e = e.into_expression();
self.with_last_node::<WireTapDefinition>("newExchangeBody", |w| w.new_exchange_body = Some(e))
}
pub fn new_exchange_header(&mut self, name: impl Into<String>, e: impl IntoExpression) -> &mut Self {
let (n, e) = (name.into(), e.into_expression());
self.with_last_node::<WireTapDefinition>("newExchangeHeader", |w| w.new_exchange_headers.push((n, e)))
}
pub fn on_prepare(&mut self, p: impl Processor + 'static) -> &mut Self {
let p: Arc<dyn Processor> = Arc::new(p);
self.with_last_node::<WireTapDefinition>("onPrepare", |w| w.on_prepare = Some(p))
}
pub fn enrich(&mut self, uri: impl Into<String>) -> &mut Self {
self.add_deferred(EnrichDefinition::new(uri_expression(uri.into())))
}
pub fn enrich_with(&mut self, uri: impl Into<String>, strategy: impl AggregationStrategy + 'static) -> &mut Self {
let mut d = EnrichDefinition::new(uri_expression(uri.into()));
d.strategy = Some(StrategyDefinition::instance(strategy));
self.add_deferred(d)
}
pub fn enrich_expr(&mut self, uri: impl IntoExpression) -> &mut Self {
self.add_deferred(EnrichDefinition::new(uri.into_expression()))
}
pub fn poll_enrich(&mut self, uri: impl Into<String>, timeout_ms: i64) -> &mut Self {
self.add_deferred(PollEnrichDefinition::new(uri_expression(uri.into()), timeout_ms))
}
pub fn poll_enrich_with(&mut self, uri: impl Into<String>, timeout_ms: i64, strategy: impl AggregationStrategy + 'static) -> &mut Self {
let mut d = PollEnrichDefinition::new(uri_expression(uri.into()), timeout_ms);
d.strategy = Some(StrategyDefinition::instance(strategy));
self.add_deferred(d)
}
pub fn poll_enrich_expr(&mut self, uri: impl IntoExpression, timeout_ms: i64) -> &mut Self {
self.add_deferred(PollEnrichDefinition::new(uri.into_expression(), timeout_ms))
}
pub fn loop_(&mut self, count: impl IntoExpression) -> &mut Self {
self.push_block(Box::new(LoopDefinition::count(count.into_expression())))
}
pub fn loop_do_while(&mut self, predicate: impl IntoExpression) -> &mut Self {
self.push_block(Box::new(LoopDefinition::do_while(predicate.into_expression())))
}
pub fn sort(&mut self, expression: impl IntoExpression) -> &mut Self {
self.add(Box::new(SortDefinition { id: None, expression: expression.into_expression(), comparator: None }))
}
pub fn sort_with<F>(&mut self, expression: impl IntoExpression, comparator: F) -> &mut Self
where
F: Fn(&Value, &Value) -> Ordering + Send + Sync + 'static,
{
self.add(Box::new(SortDefinition { id: None, expression: expression.into_expression(), comparator: Some(Arc::new(comparator)) }))
}
pub fn claim_check(&mut self, operation: ClaimCheckOperation, key: &str) -> &mut Self {
self.claim_check_with_filter(operation, key, None)
}
pub fn claim_check_with_filter(&mut self, operation: ClaimCheckOperation, key: &str, filter: Option<&str>) -> &mut Self {
let key = if key.is_empty() { None } else { Some(key.to_string()) };
self.add(Box::new(ClaimCheckDefinition { id: None, operation, key, filter: filter.map(str::to_string) }))
}
pub fn load_balance(&mut self) -> &mut Self {
self.push_block(Box::new(LoadBalanceDefinition::default()))
}
pub fn load_balance_with(&mut self, policy: LoadBalancerPolicy) -> &mut Self {
self.push_block(Box::new(LoadBalanceDefinition { id: None, policy, outputs: Default::default() }))
}
fn lb_policy(&mut self, name: &str, policy: LoadBalancerPolicy) -> &mut Self {
self.with_block::<LoadBalanceDefinition>(name, |l| l.policy = policy)
}
pub fn round_robin(&mut self) -> &mut Self {
self.lb_policy("roundRobin", LoadBalancerPolicy::RoundRobin)
}
pub fn random(&mut self) -> &mut Self {
self.lb_policy("random", LoadBalancerPolicy::Random)
}
pub fn sticky(&mut self, key: impl IntoExpression) -> &mut Self {
let k = key.into_expression();
self.lb_policy("sticky", LoadBalancerPolicy::Sticky(k))
}
pub fn weighted(&mut self, round_robin: bool, distribution_ratio: &[usize]) -> &mut Self {
self.lb_policy("weighted", LoadBalancerPolicy::Weighted { round_robin, ratios: distribution_ratio.to_vec() })
}
pub fn topic(&mut self) -> &mut Self {
self.lb_policy("topic", LoadBalancerPolicy::Topic)
}
pub fn failover(&mut self, exceptions: &[&str]) -> &mut Self {
let s = FailoverSettings { exceptions: exceptions.iter().map(|s| s.to_string()).collect(), ..Default::default() };
self.lb_policy("failover", LoadBalancerPolicy::Failover(s))
}
pub fn failover_full(&mut self, maximum_failover_attempts: i32, round_robin: bool, sticky: bool, exceptions: &[&str]) -> &mut Self {
let s = FailoverSettings {
exceptions: exceptions.iter().map(|s| s.to_string()).collect(),
maximum_failover_attempts,
round_robin,
sticky,
};
self.lb_policy("failover", LoadBalancerPolicy::Failover(s))
}
pub fn custom_load_balancer(&mut self, lb: impl LoadBalancer + 'static) -> &mut Self {
self.lb_policy("custom", LoadBalancerPolicy::Custom(Arc::new(lb)))
}
pub fn resequence(&mut self, expression: impl IntoExpression) -> &mut Self {
self.push_block(Box::new(ResequenceDefinition::new(expression.into_expression())))
}
pub fn batch(&mut self) -> &mut Self {
self.with_block::<ResequenceDefinition>("batch", |r| {
if !matches!(r.config, ResequencerConfig::Batch { .. }) {
r.config = ResequencerConfig::batch()
}
})
}
pub fn stream(&mut self) -> &mut Self {
self.with_block::<ResequenceDefinition>("stream", |r| {
if !matches!(r.config, ResequencerConfig::Stream { .. }) {
r.config = ResequencerConfig::stream()
}
})
}
pub fn batch_size(&mut self, n: usize) -> &mut Self {
self.with_block::<ResequenceDefinition>("batchSize", |r| {
if let ResequencerConfig::Batch { batch_size, .. } = &mut r.config {
*batch_size = n.max(1)
}
})
}
pub fn batch_timeout(&mut self, d: Duration) -> &mut Self {
self.with_block::<ResequenceDefinition>("batchTimeout", |r| {
if let ResequencerConfig::Batch { batch_timeout, .. } = &mut r.config {
*batch_timeout = d
}
})
}
pub fn allow_duplicates(&mut self) -> &mut Self {
self.with_block::<ResequenceDefinition>("allowDuplicates", |r| {
if let ResequencerConfig::Batch { allow_duplicates, .. } = &mut r.config {
*allow_duplicates = true
}
})
}
pub fn reverse(&mut self) -> &mut Self {
self.with_block::<ResequenceDefinition>("reverse", |r| {
if let ResequencerConfig::Batch { reverse, .. } = &mut r.config {
*reverse = true
}
})
}
pub fn capacity(&mut self, n: usize) -> &mut Self {
self.with_block::<ResequenceDefinition>("capacity", |r| {
if let ResequencerConfig::Stream { capacity, .. } = &mut r.config {
*capacity = n.max(1)
}
})
}
pub fn reject_old(&mut self) -> &mut Self {
self.with_block::<ResequenceDefinition>("rejectOld", |r| {
if let ResequencerConfig::Stream { reject_old, .. } = &mut r.config {
*reject_old = true
}
})
}
pub fn ignore_invalid_exchanges(&mut self) -> &mut Self {
self.with_block::<ResequenceDefinition>("ignoreInvalidExchanges", |r| match &mut r.config {
ResequencerConfig::Batch { ignore_invalid_exchanges, .. } | ResequencerConfig::Stream { ignore_invalid_exchanges, .. } => {
*ignore_invalid_exchanges = true
}
})
}
}