pub mod circuit_breaker;
pub mod do_try;
pub mod handler;
pub mod hook;
pub mod idempotent;
pub mod intercept;
pub mod misc;
pub mod on_completion;
pub mod on_exception;
pub mod route_policy;
pub mod throttle;
use std::sync::Arc;
use crate::builder::{ExpressionDefinition, ValueBuilder};
use crate::context::CamelContext;
use crate::error::{CamelError, Result};
use crate::message::{props, Exchange, Headers};
use crate::spi::Predicate;
use crate::value::Value;
pub use circuit_breaker::{CircuitBreakerConfig, CircuitBreakerDefinition, CircuitBreakerState};
pub use do_try::DoTryDefinition;
pub use handler::{dead_letter_channel, default_error_handler, no_error_handler, ErrorHandlerBuilder, ErrorHandlerKind, RedeliveryPolicy};
pub use hook::{CamelContextErrorExt, ErrorsRouteHook};
pub use idempotent::{memory_idempotent_repository, IdempotentConsumerDefinition, IdempotentRepository, MemoryIdempotentRepository};
pub use intercept::{InterceptDefinition, InterceptKind};
pub use misc::{Policy, ThreadsOptions};
pub use on_completion::{OnCompletionDefinition, OnCompletionMode};
pub use on_exception::OnExceptionDefinition;
pub use route_policy::{RoutePolicy, ThrottlingInflightRoutePolicy, ThrottlingScope};
pub const TRY_ROUTE_BLOCK: &str = "CamelTryRouteBlock";
pub const ROLLBACK_ONLY_LAST: &str = "CamelRollbackOnlyLast";
pub const ORIGINAL_MESSAGE: &str = "CamelErrorHandlerOriginalMessage";
pub const ON_COMPLETION_REGISTERED: &str = "CamelOnCompletionRegistered";
pub fn error_to_value(e: &CamelError) -> Value {
let mut items: Vec<(&str, Value)> = vec![("kind", Value::from(e.kind.as_str())), ("message", Value::from(e.message.as_str()))];
if let Some(c) = &e.cause {
items.push(("cause", error_to_value(c)));
}
Value::object(items)
}
pub fn value_to_error(v: &Value) -> Option<CamelError> {
let kind = v.get("kind")?.as_str()?.to_string();
let message = v.get("message").map(|m| m.to_string()).unwrap_or_default();
let mut e = CamelError::new(kind, message);
if let Some(c) = v.get("cause").and_then(value_to_error) {
e = e.with_cause(c);
}
Some(e)
}
pub fn caught_exception(ex: &Exchange) -> Option<CamelError> {
ex.property(props::EXCEPTION_CAUGHT).and_then(value_to_error)
}
pub fn set_caught_exception(ex: &mut Exchange, e: &CamelError) {
ex.set_property(props::EXCEPTION_CAUGHT, error_to_value(e));
}
pub fn is_error_handler_suppressed(ex: &Exchange) -> bool {
ex.property(TRY_ROUTE_BLOCK).and_then(|v| v.as_i64()).unwrap_or(0) > 0
}
pub(crate) fn enter_suppressed(ex: &mut Exchange) {
let n = ex.property(TRY_ROUTE_BLOCK).and_then(|v| v.as_i64()).unwrap_or(0);
ex.set_property(TRY_ROUTE_BLOCK, n + 1);
}
pub(crate) fn exit_suppressed(ex: &mut Exchange) {
let n = ex.property(TRY_ROUTE_BLOCK).and_then(|v| v.as_i64()).unwrap_or(0);
if n <= 1 {
ex.remove_property(TRY_ROUTE_BLOCK);
} else {
ex.set_property(TRY_ROUTE_BLOCK, n - 1);
}
}
pub fn is_exhausted(ex: &Exchange) -> bool {
ex.property(props::REDELIVERY_EXHAUSTED).map(|v| v.is_truthy()).unwrap_or(false)
}
pub(crate) fn snapshot_message(ex: &Exchange) -> Value {
Value::object([("body", ex.message.body.clone()), ("headers", ex.message.headers.to_value())])
}
pub(crate) const ALLOW_USE_ORIGINAL_MESSAGE: &str = "CamelAllowUseOriginalMessage";
pub(crate) fn begin_child_unit_of_work(ex: &mut Exchange) {
if ex.properties.contains_key(ORIGINAL_MESSAGE) {
let snap = snapshot_message(ex);
ex.set_property(ORIGINAL_MESSAGE, snap);
}
}
pub(crate) fn restore_message(ex: &mut Exchange, snap: &Value, headers_too: bool) {
if let Some(b) = snap.get("body") {
ex.message.body = b.clone();
}
if headers_too {
if let Some(h) = snap.get("headers").and_then(|h| h.as_object()) {
ex.message.headers = h.iter().map(|(k, v)| (k.clone(), v.clone())).collect::<Headers>();
}
}
}
#[derive(Clone, Debug)]
pub enum BoolOrPredicate {
Bool(bool),
Predicate(ExpressionDefinition),
}
impl BoolOrPredicate {
pub(crate) fn resolve(&self, ctx: &CamelContext) -> Result<ResolvedBool> {
Ok(match self {
BoolOrPredicate::Bool(b) => ResolvedBool::Const(*b),
BoolOrPredicate::Predicate(p) => ResolvedBool::Pred(p.resolve_predicate(ctx)?),
})
}
}
pub trait IntoBoolOrPredicate {
fn into_bool_or_predicate(self) -> BoolOrPredicate;
}
impl IntoBoolOrPredicate for bool {
fn into_bool_or_predicate(self) -> BoolOrPredicate {
BoolOrPredicate::Bool(self)
}
}
impl IntoBoolOrPredicate for ValueBuilder {
fn into_bool_or_predicate(self) -> BoolOrPredicate {
BoolOrPredicate::Predicate(self.def)
}
}
impl IntoBoolOrPredicate for ExpressionDefinition {
fn into_bool_or_predicate(self) -> BoolOrPredicate {
BoolOrPredicate::Predicate(self)
}
}
#[derive(Clone)]
pub(crate) enum ResolvedBool {
Const(bool),
Pred(Arc<dyn Predicate>),
}
impl ResolvedBool {
pub(crate) fn eval(&self, ex: &Exchange) -> bool {
match self {
ResolvedBool::Const(b) => *b,
ResolvedBool::Pred(p) => p.matches(ex).unwrap_or(false),
}
}
}
pub(crate) fn matches_opt(p: &Option<Arc<dyn Predicate>>, ex: &Exchange) -> bool {
match p {
None => true,
Some(p) => match p.matches(ex) {
Ok(b) => b,
Err(e) => {
crate::camel_warn!("ErrorHandler", "Error evaluating predicate: {}", e);
false
}
},
}
}
pub(crate) fn resolve_opt_pred(d: &Option<ExpressionDefinition>, ctx: &CamelContext) -> Result<Option<Arc<dyn Predicate>>> {
d.as_ref().map(|d| d.resolve_predicate(ctx)).transpose()
}
pub(crate) fn log_at(level: crate::log::Level, logger: &str, msg: impl FnOnce() -> String) {
if crate::log::enabled(level) {
crate::log::log(level, logger, msg());
}
}
pub(crate) fn random_f64() -> f64 {
use std::sync::atomic::{AtomicU64, Ordering};
static SEED: AtomicU64 = AtomicU64::new(0);
let mut s = SEED.load(Ordering::Relaxed);
if s == 0 {
s = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_nanos() as u64).unwrap_or(1) | 1;
}
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
SEED.store(s, Ordering::Relaxed);
(s >> 11) as f64 / (1u64 << 53) as f64
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_value_roundtrip() {
let e = CamelError::io("outer").with_cause(CamelError::illegal_argument("inner"));
let v = error_to_value(&e);
assert_eq!(v.get("message").unwrap().as_str(), Some("outer"));
assert_eq!(value_to_error(&v).unwrap(), e);
}
#[test]
fn random_in_range() {
for _ in 0..100 {
let r = random_f64();
assert!((0.0..1.0).contains(&r));
}
}
}