1use std::any::{Any, TypeId};
2use std::collections::HashMap;
3use std::sync::Arc;
4use tokio_util::sync::CancellationToken;
5use tracing::{debug, trace};
6
7#[cfg(test)]
8use camel_api::StepLifecycle;
9use camel_api::component_metadata::ComponentMetadata;
10use camel_api::error_handler::ErrorHandlerConfig;
11use camel_api::{
12 CamelError, FunctionInvoker, HealthReport, Lifecycle, MetricsCollector, MetricsHandle,
13 PlatformIdentity, PlatformService, ReadinessGate, RouteTemplateSpec, RuntimeCommandBus,
14 RuntimeQueryBus, TemplateInstanceRecord,
15};
16use camel_component_api::{Component, ComponentContext, ComponentRegistrar};
17use camel_language_api::Language;
18
19use crate::health_registry::HealthCheckRegistry;
20use crate::intercept::InterceptRules;
21use crate::language_registry::LanguageRegistryError;
22use crate::lifecycle::adapters::controller_actor::RouteControllerHandle;
23use crate::lifecycle::adapters::route_controller::SharedLanguageRegistry;
24use crate::lifecycle::application::route_definition::RouteDefinition;
25use crate::lifecycle::application::runtime_bus::RuntimeBus;
26use crate::registry::RegistryError;
27use crate::shared::components::domain::Registry;
28use crate::shared::observability::domain::{MetricsLeversConfig, TracerConfig};
29use crate::startup_validation::ConfigCheck;
30use crate::template::TemplateRegistry;
31
32pub use crate::context_builder::CamelContextBuilder;
33
34pub struct CamelContext {
43 registry: Arc<std::sync::Mutex<Registry>>,
44 route_controller: RouteControllerHandle,
45 actor_join: Option<tokio::task::JoinHandle<()>>,
46 supervision_join: Option<tokio::task::JoinHandle<()>>,
47 runtime: Arc<RuntimeBus>,
48 cancel_token: CancellationToken,
49 metrics: Arc<MetricsHandle>,
54 metrics_levers: MetricsLeversConfig,
60 platform_service: Arc<dyn PlatformService>,
62 languages: SharedLanguageRegistry,
63 shutdown_timeout: std::time::Duration,
64 services: Vec<Box<dyn Lifecycle>>,
65 health_registry: Arc<HealthCheckRegistry>,
66 component_configs: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
67 function_invoker: Option<Arc<dyn FunctionInvoker>>,
68 template_registry: Arc<TemplateRegistry>,
69 idempotent_repositories: crate::registry::SharedIdempotentRegistry,
70 claim_check_repositories: crate::registry::SharedClaimCheckRegistry,
71 cache_repositories: crate::registry::SharedCacheRegistry,
72 startup_checks: Vec<Box<dyn ConfigCheck>>,
76 build_version: &'static str,
80 build_git_sha: &'static str,
81 build_started_at: std::time::Instant,
83}
84
85pub(crate) struct FromParts {
88 pub(crate) registry: Arc<std::sync::Mutex<Registry>>,
89 pub(crate) route_controller: RouteControllerHandle,
90 pub(crate) _actor_join: tokio::task::JoinHandle<()>,
91 pub(crate) supervision_join: Option<tokio::task::JoinHandle<()>>,
92 pub(crate) runtime: Arc<RuntimeBus>,
93 pub(crate) cancel_token: CancellationToken,
94 pub(crate) metrics: Arc<MetricsHandle>,
95 pub(crate) platform_service: Arc<dyn PlatformService>,
96 pub(crate) languages: SharedLanguageRegistry,
97 pub(crate) shutdown_timeout: std::time::Duration,
98 pub(crate) services: Vec<Box<dyn Lifecycle>>,
99 pub(crate) health_registry: Arc<HealthCheckRegistry>,
100 pub(crate) component_configs: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
101 pub(crate) function_invoker: Option<Arc<dyn FunctionInvoker>>,
102 pub(crate) template_registry: Arc<TemplateRegistry>,
103 pub(crate) idempotent_repositories: crate::registry::SharedIdempotentRegistry,
104 pub(crate) claim_check_repositories: crate::registry::SharedClaimCheckRegistry,
105 pub(crate) cache_repositories: crate::registry::SharedCacheRegistry,
106 pub(crate) startup_checks: Vec<Box<dyn ConfigCheck>>,
107 pub(crate) build_version: &'static str,
108 pub(crate) build_git_sha: &'static str,
109 pub(crate) build_started_at: std::time::Instant,
110}
111
112impl CamelContext {
113 pub(crate) fn from_parts(parts: FromParts) -> Self {
114 Self {
115 registry: parts.registry,
116 route_controller: parts.route_controller,
117 actor_join: Some(parts._actor_join),
118 supervision_join: parts.supervision_join,
119 runtime: parts.runtime,
120 cancel_token: parts.cancel_token,
121 metrics: parts.metrics,
122 metrics_levers: MetricsLeversConfig::default(),
123 platform_service: parts.platform_service,
124 languages: parts.languages,
125 shutdown_timeout: parts.shutdown_timeout,
126 services: parts.services,
127 health_registry: parts.health_registry,
128 component_configs: parts.component_configs,
129 function_invoker: parts.function_invoker,
130 template_registry: parts.template_registry,
131 idempotent_repositories: parts.idempotent_repositories,
132 claim_check_repositories: parts.claim_check_repositories,
133 cache_repositories: parts.cache_repositories,
134 startup_checks: parts.startup_checks,
135 build_version: parts.build_version,
136 build_git_sha: parts.build_git_sha,
137 build_started_at: parts.build_started_at,
138 }
139 }
140}
141
142#[derive(Clone)]
146pub struct RuntimeExecutionHandle {
147 pub(crate) controller: RouteControllerHandle,
148 pub(crate) runtime: Arc<RuntimeBus>,
149 pub(crate) function_invoker: Option<Arc<dyn FunctionInvoker>>,
150 #[cfg(test)]
154 #[allow(clippy::type_complexity)]
155 pub(crate) test_lifecycle_inject: Arc<std::sync::Mutex<Option<Vec<Arc<dyn StepLifecycle>>>>>,
156}
157
158impl RuntimeExecutionHandle {
159 pub(crate) async fn add_route_definition(
160 &self,
161 definition: RouteDefinition,
162 ) -> Result<(), CamelError> {
163 use crate::lifecycle::application::ports::RouteRegistrationPort;
164 self.runtime
165 .register_route(definition)
166 .await
167 .map_err(Into::into)
168 }
169
170 #[allow(dead_code)]
173 pub(crate) async fn compile_route_definition(
174 &self,
175 definition: RouteDefinition,
176 ) -> Result<camel_api::BoxProcessor, CamelError> {
177 self.controller.compile_route_definition(definition).await
178 }
179
180 #[allow(dead_code)] pub(crate) async fn compile_route_definition_with_generation(
182 &self,
183 definition: RouteDefinition,
184 generation: u64,
185 ) -> Result<camel_api::BoxProcessor, CamelError> {
186 self.controller
187 .compile_route_definition_with_generation(definition, generation)
188 .await
189 }
190
191 pub(crate) async fn compile_route_definition_pipeline(
192 &self,
193 definition: RouteDefinition,
194 generation: u64,
195 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
196 self.controller
197 .compile_route_definition_pipeline(definition, generation)
198 .await
199 }
200
201 pub(crate) async fn compile_route_definition_dry_pipeline(
204 &self,
205 definition: RouteDefinition,
206 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
207 self.controller
208 .compile_route_definition_dry_pipeline(definition)
209 .await
210 }
211
212 pub(crate) async fn prepare_route_definition_with_generation(
213 &self,
214 definition: RouteDefinition,
215 generation: u64,
216 ) -> Result<crate::lifecycle::domain::route_compilation::PreparedRoute, CamelError> {
217 self.controller
218 .prepare_route_definition_with_generation(definition, generation)
219 .await
220 }
221
222 pub(crate) async fn insert_prepared_route(
223 &self,
224 prepared: crate::lifecycle::domain::route_compilation::PreparedRoute,
225 ) -> Result<(), CamelError> {
226 self.controller.insert_prepared_route(prepared).await
227 }
228
229 pub(crate) async fn discard_prepared_staging(&self, route_id: &str) -> Result<(), CamelError> {
230 self.controller.discard_prepared_staging(route_id).await
231 }
232
233 pub(crate) async fn remove_route_preserving_functions(
234 &self,
235 route_id: String,
236 ) -> Result<(), CamelError> {
237 self.controller
238 .remove_route_preserving_functions(route_id)
239 .await
240 }
241
242 pub(crate) async fn register_route_aggregate(
243 &self,
244 route_id: String,
245 ) -> Result<(), CamelError> {
246 self.runtime.register_aggregate_only(route_id).await
247 }
248
249 pub(crate) async fn swap_route_pipeline(
250 &self,
251 route_id: &str,
252 pipeline: camel_api::BoxProcessor,
253 ) -> Result<(), CamelError> {
254 self.controller.swap_pipeline(route_id, pipeline).await
255 }
256
257 pub(crate) async fn stop_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
259 self.controller.stop_route_reload(route_id).await
260 }
261
262 pub(crate) async fn start_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
264 self.controller.start_route_reload(route_id).await
265 }
266
267 pub(crate) async fn swap_route_pipeline_raw(
270 &self,
271 route_id: &str,
272 pipeline: camel_api::BoxProcessor,
273 lifecycle: Vec<Arc<dyn camel_api::StepLifecycle>>,
274 ) -> Result<(), CamelError> {
275 self.controller
276 .swap_pipeline_raw(route_id, pipeline, lifecycle)
277 .await
278 }
279
280 pub(crate) async fn execute_runtime_command(
281 &self,
282 cmd: camel_api::RuntimeCommand,
283 ) -> Result<camel_api::RuntimeCommandResult, CamelError> {
284 self.runtime.execute(cmd).await
285 }
286
287 pub(crate) async fn runtime_route_status(
288 &self,
289 route_id: &str,
290 ) -> Result<Option<String>, CamelError> {
291 match self
292 .runtime
293 .ask(camel_api::RuntimeQuery::GetRouteStatus {
294 route_id: route_id.to_string(),
295 })
296 .await
297 {
298 Ok(camel_api::RuntimeQueryResult::RouteStatus { status, .. }) => Ok(Some(status)),
299 Ok(_) => Err(CamelError::RouteError(
300 "unexpected runtime query response for route status".to_string(),
301 )),
302 Err(CamelError::RouteError(msg)) if msg.contains("not found") => Ok(None),
303 Err(err) => Err(err),
304 }
305 }
306
307 pub(crate) async fn runtime_route_ids(&self) -> Result<Vec<String>, CamelError> {
308 match self.runtime.ask(camel_api::RuntimeQuery::ListRoutes).await {
309 Ok(camel_api::RuntimeQueryResult::Routes { route_ids }) => Ok(route_ids),
310 Ok(_) => Err(CamelError::RouteError(
311 "unexpected runtime query response for route listing".to_string(),
312 )),
313 Err(err) => Err(err),
314 }
315 }
316
317 pub(crate) async fn route_source_hash(&self, route_id: &str) -> Option<u64> {
318 self.controller.route_source_hash(route_id).await
319 }
320
321 pub(crate) async fn in_flight_count(&self, route_id: &str) -> Result<u64, CamelError> {
322 if !self.controller.route_exists(route_id).await? {
323 return Err(CamelError::RouteError(format!(
324 "Route '{}' not found",
325 route_id
326 )));
327 }
328 Ok(self
329 .controller
330 .in_flight_count(route_id)
331 .await?
332 .unwrap_or(0))
333 }
334
335 pub(crate) async fn route_has_lifecycle(&self, route_id: &str) -> bool {
337 self.controller
338 .route_has_lifecycle(route_id)
339 .await
340 .unwrap_or(false)
341 }
342
343 pub(crate) fn function_invoker(&self) -> Option<Arc<dyn FunctionInvoker>> {
344 self.function_invoker.clone()
345 }
346
347 #[cfg(test)]
348 pub(crate) async fn force_start_route_for_test(
349 &self,
350 route_id: &str,
351 ) -> Result<(), CamelError> {
352 self.controller.start_route(route_id).await
353 }
354
355 pub async fn controller_route_count_for_test(&self) -> usize {
356 self.controller.route_count().await.unwrap_or(0)
357 }
358}
359
360#[async_trait::async_trait]
361impl crate::hot_reload::ports::ReloadExecutorPort for RuntimeExecutionHandle {
362 async fn add_route_definition(&self, definition: RouteDefinition) -> Result<(), CamelError> {
363 RuntimeExecutionHandle::add_route_definition(self, definition).await
364 }
365
366 async fn compile_route_definition_pipeline(
367 &self,
368 definition: RouteDefinition,
369 generation: u64,
370 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
371 RuntimeExecutionHandle::compile_route_definition_pipeline(self, definition, generation)
372 .await
373 }
374
375 async fn compile_route_definition_dry_pipeline(
376 &self,
377 definition: RouteDefinition,
378 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
379 RuntimeExecutionHandle::compile_route_definition_dry_pipeline(self, definition).await
380 }
381
382 async fn prepare_route_definition_with_generation(
383 &self,
384 definition: RouteDefinition,
385 generation: u64,
386 ) -> Result<crate::lifecycle::domain::route_compilation::PreparedRoute, CamelError> {
387 RuntimeExecutionHandle::prepare_route_definition_with_generation(
388 self, definition, generation,
389 )
390 .await
391 }
392
393 async fn insert_prepared_route(
394 &self,
395 prepared: crate::lifecycle::domain::route_compilation::PreparedRoute,
396 ) -> Result<(), CamelError> {
397 RuntimeExecutionHandle::insert_prepared_route(self, prepared).await
398 }
399
400 async fn discard_prepared_staging(&self, route_id: &str) -> Result<(), CamelError> {
401 RuntimeExecutionHandle::discard_prepared_staging(self, route_id).await
402 }
403
404 async fn remove_route_preserving_functions(&self, route_id: String) -> Result<(), CamelError> {
405 RuntimeExecutionHandle::remove_route_preserving_functions(self, route_id).await
406 }
407
408 async fn register_route_aggregate(&self, route_id: String) -> Result<(), CamelError> {
409 RuntimeExecutionHandle::register_route_aggregate(self, route_id).await
410 }
411
412 async fn swap_route_pipeline(
413 &self,
414 route_id: &str,
415 pipeline: camel_api::BoxProcessor,
416 ) -> Result<(), CamelError> {
417 RuntimeExecutionHandle::swap_route_pipeline(self, route_id, pipeline).await
418 }
419
420 async fn stop_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
421 RuntimeExecutionHandle::stop_route_reload(self, route_id).await
422 }
423
424 async fn start_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
425 RuntimeExecutionHandle::start_route_reload(self, route_id).await
426 }
427
428 async fn swap_route_pipeline_raw(
429 &self,
430 route_id: &str,
431 pipeline: camel_api::BoxProcessor,
432 lifecycle: Vec<std::sync::Arc<dyn camel_api::StepLifecycle>>,
433 ) -> Result<(), CamelError> {
434 RuntimeExecutionHandle::swap_route_pipeline_raw(self, route_id, pipeline, lifecycle).await
435 }
436
437 async fn execute_runtime_command(
438 &self,
439 cmd: camel_api::RuntimeCommand,
440 ) -> Result<camel_api::RuntimeCommandResult, CamelError> {
441 RuntimeExecutionHandle::execute_runtime_command(self, cmd).await
442 }
443
444 async fn runtime_route_status(&self, route_id: &str) -> Result<Option<String>, CamelError> {
445 RuntimeExecutionHandle::runtime_route_status(self, route_id).await
446 }
447
448 async fn in_flight_count(&self, route_id: &str) -> Result<u64, CamelError> {
449 RuntimeExecutionHandle::in_flight_count(self, route_id).await
450 }
451
452 async fn route_has_lifecycle(&self, route_id: &str) -> bool {
453 RuntimeExecutionHandle::route_has_lifecycle(self, route_id).await
454 }
455
456 #[cfg(test)]
457 fn take_test_lifecycle_inject(
458 &self,
459 ) -> Option<Vec<std::sync::Arc<dyn camel_api::StepLifecycle>>> {
460 self.test_lifecycle_inject.lock().unwrap().take()
461 }
462}
463
464impl CamelContext {
465 pub fn builder() -> CamelContextBuilder {
466 CamelContextBuilder::new()
467 }
468
469 pub async fn set_error_handler(&mut self, config: ErrorHandlerConfig) {
471 let _ = self.route_controller.set_error_handler(config).await;
472 }
473
474 pub async fn set_bind_exposure_acks(
478 &mut self,
479 acks: crate::lifecycle::adapters::route_controller_trait::BindExposureAcks,
480 ) {
481 let _ = self.route_controller.set_bind_exposure_acks(acks).await;
482 }
483
484 pub async fn set_tracing(&mut self, enabled: bool) {
486 let config = TracerConfig {
487 enabled,
488 ..Default::default()
489 };
490 self.metrics_levers = config.metrics_levers.clone();
493 let _ = self.route_controller.set_tracer_config(config).await;
494 }
495
496 pub async fn set_tracer_config(&mut self, config: TracerConfig) {
498 self.metrics_levers = config.metrics_levers.clone();
501 let _ = self.route_controller.set_tracer_config(config).await;
502 }
503
504 pub async fn with_tracing(mut self) -> Self {
506 self.set_tracing(true).await;
507 self
508 }
509
510 pub async fn with_tracer_config(mut self, config: TracerConfig) -> Self {
514 self.set_tracer_config(config).await;
515 self
516 }
517
518 pub fn with_lifecycle<L: Lifecycle + 'static>(mut self, service: L) -> Self {
527 self.add_lifecycle(service);
528 self
529 }
530
531 pub fn add_lifecycle<L: Lifecycle + 'static>(&mut self, service: L) {
536 if let Some(collector) = service.as_metrics_collector() {
537 self.metrics.register(collector);
541 self.metrics
546 .record_build_info(self.build_version, self.build_git_sha);
547 self.metrics
548 .record_uptime(self.build_started_at.elapsed().as_secs_f64());
549 }
550 if let Some(invoker) = service.as_function_invoker() {
551 self.function_invoker = Some(invoker.clone());
552 if let Err(e) = self.route_controller.try_set_function_invoker(invoker) {
553 tracing::debug!("Failed to propagate function invoker to route controller: {e}");
554 }
555 }
556
557 self.services.push(Box::new(service));
558 }
559
560 pub fn register_component<C: Component + 'static>(&mut self, component: C) {
566 self.register_component_dyn(Arc::new(component));
567 }
568
569 pub async fn set_intercept_rules(&self, rules: InterceptRules) -> Result<(), CamelError> {
576 self.route_controller.set_intercept_rules(rules).await
577 }
578
579 pub fn add_startup_check(&mut self, check: Box<dyn ConfigCheck>) {
588 self.startup_checks.push(check);
589 }
590
591 pub fn register_language(
598 &mut self,
599 name: impl Into<String>,
600 lang: Box<dyn Language>,
601 ) -> Result<(), LanguageRegistryError> {
602 let name = name.into();
603 let mut languages = self
604 .languages
605 .lock()
606 .expect("mutex poisoned: another thread panicked while holding this lock"); if languages.contains_key(&name) {
608 return Err(LanguageRegistryError::AlreadyRegistered { name });
609 }
610 languages.insert(name, Arc::from(lang));
611 Ok(())
612 }
613
614 pub fn resolve_language(&self, name: &str) -> Option<Arc<dyn Language>> {
616 let languages = self
617 .languages
618 .lock()
619 .expect("mutex poisoned: another thread panicked while holding this lock"); languages.get(name).cloned()
621 }
622
623 pub async fn add_route_definition(
627 &self,
628 definition: RouteDefinition,
629 ) -> Result<(), CamelError> {
630 use crate::lifecycle::application::ports::RouteRegistrationPort;
631 debug!(
632 from = definition.from_uri(),
633 route_id = %definition.route_id(),
634 "Adding route definition"
635 );
636 self.runtime
637 .register_route(definition)
638 .await
639 .map_err(Into::into)
640 }
641
642 pub fn registry(&self) -> std::sync::MutexGuard<'_, Registry> {
644 self.registry
645 .lock()
646 .expect("mutex poisoned: another thread panicked while holding this lock") }
648
649 pub fn registry_arc(&self) -> Arc<std::sync::Mutex<Registry>> {
651 Arc::clone(&self.registry)
652 }
653
654 pub fn runtime_execution_handle(&self) -> RuntimeExecutionHandle {
656 RuntimeExecutionHandle {
657 controller: self.route_controller.clone(),
658 runtime: Arc::clone(&self.runtime),
659 function_invoker: self.function_invoker.clone(),
660 #[cfg(test)]
661 test_lifecycle_inject: Arc::new(std::sync::Mutex::new(None)),
662 }
663 }
664
665 pub fn metrics(&self) -> Arc<dyn MetricsCollector> {
667 Arc::clone(&self.metrics) as Arc<dyn MetricsCollector>
668 }
669
670 pub fn platform_service(&self) -> Arc<dyn PlatformService> {
672 Arc::clone(&self.platform_service)
673 }
674
675 pub fn readiness_gate(&self) -> Arc<dyn ReadinessGate> {
677 self.platform_service.readiness_gate()
678 }
679
680 pub fn platform_identity(&self) -> PlatformIdentity {
682 self.platform_service.identity()
683 }
684
685 pub fn leadership(&self) -> Arc<dyn camel_api::LeadershipService> {
687 self.platform_service.leadership()
688 }
689
690 pub fn runtime(&self) -> Arc<dyn camel_api::RuntimeHandle> {
692 self.runtime.clone()
693 }
694
695 pub fn producer_context(&self) -> camel_api::ProducerContext {
697 camel_api::ProducerContext::new().with_runtime(self.runtime())
698 }
699
700 pub async fn runtime_route_status(&self, route_id: &str) -> Result<Option<String>, CamelError> {
702 match self
703 .runtime()
704 .ask(camel_api::RuntimeQuery::GetRouteStatus {
705 route_id: route_id.to_string(),
706 })
707 .await
708 {
709 Ok(camel_api::RuntimeQueryResult::RouteStatus { status, .. }) => Ok(Some(status)),
710 Ok(_) => Err(CamelError::RouteError(
711 "unexpected runtime query response for route status".to_string(),
712 )),
713 Err(CamelError::RouteError(msg)) if msg.contains("not found") => Ok(None),
714 Err(err) => Err(err),
715 }
716 }
717
718 pub async fn start(&mut self) -> Result<(), CamelError> {
726 crate::lifecycle::application::context_lifecycle::start_context(
727 &mut self.services,
728 &mut self.startup_checks,
729 &self.runtime,
730 &self.route_controller,
731 &mut self.cancel_token,
732 )
733 .await?;
734 self.route_controller.mark_started().await
738 }
739
740 pub async fn stop(&mut self) -> Result<(), CamelError> {
742 self.stop_timeout(self.shutdown_timeout).await
743 }
744
745 pub async fn stop_timeout(&mut self, _timeout: std::time::Duration) -> Result<(), CamelError> {
755 crate::lifecycle::application::context_lifecycle::stop_context(
756 &self.cancel_token,
757 &mut self.supervision_join,
758 &self.runtime,
759 &self.route_controller,
760 &mut self.services,
761 )
762 .await
763 }
764
765 pub fn shutdown_timeout(&self) -> std::time::Duration {
767 self.shutdown_timeout
768 }
769
770 pub fn set_shutdown_timeout(&mut self, timeout: std::time::Duration) {
772 self.shutdown_timeout = timeout;
773 }
774
775 #[cfg(test)]
778 pub(crate) fn take_actor_join(&mut self) -> Option<tokio::task::JoinHandle<()>> {
779 self.actor_join.take()
780 }
781
782 pub async fn abort(&mut self) {
790 crate::lifecycle::application::context_lifecycle::abort_context(
791 &self.cancel_token,
792 &mut self.supervision_join,
793 &self.runtime,
794 &self.route_controller as &dyn crate::lifecycle::application::ports::RouteOrderingPort,
795 &self.route_controller
796 as &dyn crate::lifecycle::application::ports::RouteDestructiveTeardownPort,
797 &mut self.services,
798 self.health_registry.cancel_token(),
799 &mut self.actor_join,
800 )
801 .await
802 }
803
804 pub async fn health_check(&self) -> HealthReport {
806 use camel_api::HealthSource;
807 self.health_report().await
808 }
809
810 pub fn health_registry(&self) -> Arc<HealthCheckRegistry> {
811 Arc::clone(&self.health_registry)
812 }
813
814 pub fn set_component_config<T: 'static + Send + Sync>(&mut self, config: T) {
816 self.component_configs
817 .insert(TypeId::of::<T>(), Box::new(config));
818 }
819
820 pub fn get_component_config<T: 'static + Send + Sync>(&self) -> Option<&T> {
822 self.component_configs
823 .get(&TypeId::of::<T>())
824 .and_then(|b| b.downcast_ref::<T>())
825 }
826
827 pub fn component_metadata(&self, scheme: &str) -> Option<ComponentMetadata> {
831 self.registry.lock().ok()?.get_metadata(scheme)
832 }
833
834 pub fn all_component_metadata(&self) -> Vec<ComponentMetadata> {
836 self.registry
837 .lock()
838 .expect("mutex poisoned: another thread panicked while holding this lock") .all_metadata()
840 }
841
842 pub fn metadata_catalog(
849 &self,
850 ) -> crate::component_metadata_catalog::RuntimeComponentMetadataCatalog {
851 crate::component_metadata_catalog::RuntimeComponentMetadataCatalog::new(Arc::clone(
852 &self.registry,
853 ))
854 }
855
856 pub fn add_route_template(&self, spec: RouteTemplateSpec) -> Result<(), CamelError> {
862 self.template_registry.register(spec)
863 }
864
865 pub fn get_route_template(&self, id: &str) -> Option<RouteTemplateSpec> {
867 self.template_registry.get(id)
868 }
869
870 pub fn template_ids(&self) -> Vec<String> {
872 self.template_registry.template_ids()
873 }
874
875 pub fn record_template_instance(&self, record: TemplateInstanceRecord) {
877 self.template_registry.record_instance(record)
878 }
879
880 pub fn template_instances(&self, template_id: &str) -> Vec<TemplateInstanceRecord> {
882 self.template_registry.instances(template_id)
883 }
884
885 pub fn register_idempotent_repository(
892 &mut self,
893 name: impl Into<String>,
894 repo: Arc<dyn camel_api::IdempotentRepository>,
895 ) -> Result<(), RegistryError> {
896 self.idempotent_repositories.register(name, repo)
897 }
898
899 pub fn idempotent_repository(
901 &self,
902 name: &str,
903 ) -> Option<Arc<dyn camel_api::IdempotentRepository>> {
904 self.idempotent_repositories.get(name)
905 }
906
907 pub fn register_claim_check_repository(
914 &mut self,
915 name: impl Into<String>,
916 repo: Arc<dyn camel_api::ClaimCheckRepository>,
917 ) -> Result<(), RegistryError> {
918 self.claim_check_repositories.register(name, repo)
919 }
920
921 pub fn claim_check_repository(
923 &self,
924 name: &str,
925 ) -> Option<Arc<dyn camel_api::ClaimCheckRepository>> {
926 self.claim_check_repositories.get(name)
927 }
928
929 pub fn register_cache_repository(
936 &mut self,
937 name: impl Into<String>,
938 repo: Arc<dyn camel_api::CacheRepository>,
939 ) -> Result<(), RegistryError> {
940 self.cache_repositories.register(name, repo)
941 }
942
943 pub fn replace_cache_repository(
947 &mut self,
948 name: impl Into<String>,
949 repo: Arc<dyn camel_api::CacheRepository>,
950 ) -> Option<Arc<dyn camel_api::CacheRepository>> {
951 self.cache_repositories.register_or_replace(name, repo)
952 }
953
954 pub fn cache_repository(&self, name: &str) -> Option<Arc<dyn camel_api::CacheRepository>> {
956 self.cache_repositories.get(name)
957 }
958
959 pub fn shutdown_token(&self) -> CancellationToken {
964 self.cancel_token.clone()
965 }
966}
967
968impl ComponentRegistrar for CamelContext {
969 fn register_component_dyn(&mut self, component: Arc<dyn Component>) {
970 let scheme = component.scheme().to_string();
971 self.registry
972 .lock()
973 .expect("mutex poisoned: another thread panicked while holding this lock") .register(component);
975 trace!(scheme, "Registered component");
976 }
977}
978
979impl ComponentContext for CamelContext {
980 fn resolve_component(&self, scheme: &str) -> Option<Arc<dyn Component>> {
981 self.registry.lock().ok()?.get(scheme)
982 }
983
984 fn resolve_language(&self, name: &str) -> Option<Arc<dyn Language>> {
985 self.languages.lock().ok()?.get(name).cloned()
986 }
987
988 fn metrics(&self) -> Arc<dyn MetricsCollector> {
989 Arc::clone(&self.metrics) as Arc<dyn MetricsCollector>
990 }
991
992 fn component_metrics_enabled(&self) -> bool {
998 self.metrics_levers.components_enabled()
999 }
1000
1001 fn health(&self) -> Arc<dyn camel_component_api::HealthCheckRegistry> {
1002 Arc::clone(&self.health_registry) as Arc<dyn camel_component_api::HealthCheckRegistry>
1005 }
1006
1007 fn platform_service(&self) -> Arc<dyn PlatformService> {
1008 Arc::clone(&self.platform_service)
1009 }
1010
1011 fn register_route_health_check(
1012 &self,
1013 route_id: &str,
1014 check: Arc<dyn camel_api::AsyncHealthCheck>,
1015 ) {
1016 self.health_registry.register_for_route(route_id, check);
1017 }
1018
1019 fn unregister_route_health_check(&self, route_id: &str) {
1020 self.health_registry.unregister_for_route(route_id);
1021 }
1022}
1023
1024#[async_trait::async_trait]
1025impl camel_api::HealthSource for CamelContext {
1026 async fn liveness(&self) -> camel_api::HealthStatus {
1027 let has_failed = self
1028 .services
1029 .iter()
1030 .any(|s| s.status() == camel_api::ServiceStatus::Failed);
1031 if has_failed {
1032 camel_api::HealthStatus::Unhealthy
1033 } else {
1034 camel_api::HealthStatus::Healthy
1035 }
1036 }
1037
1038 async fn readiness(&self) -> camel_api::HealthStatus {
1039 let has_failed = self
1040 .services
1041 .iter()
1042 .any(|s| s.status() == camel_api::ServiceStatus::Failed);
1043 if has_failed {
1044 return camel_api::HealthStatus::Unhealthy;
1045 }
1046 let has_stopped = self
1047 .services
1048 .iter()
1049 .any(|s| s.status() == camel_api::ServiceStatus::Stopped);
1050 if has_stopped {
1051 return camel_api::HealthStatus::Degraded;
1052 }
1053 self.health_registry.check_all().await.status
1054 }
1055
1056 async fn health_report(&self) -> camel_api::HealthReport {
1057 let mut report = self.health_registry.check_all().await;
1058 let mut worst = report.status;
1059 for service in &self.services {
1060 let svc_status = service.status();
1061 let health = match svc_status {
1062 camel_api::ServiceStatus::Started => camel_api::HealthStatus::Healthy,
1063 camel_api::ServiceStatus::Stopped => camel_api::HealthStatus::Degraded,
1064 camel_api::ServiceStatus::Failed => camel_api::HealthStatus::Unhealthy,
1065 _ => camel_api::HealthStatus::Unhealthy,
1068 };
1069 if matches!(worst, camel_api::HealthStatus::Healthy)
1070 && matches!(
1071 health,
1072 camel_api::HealthStatus::Degraded | camel_api::HealthStatus::Unhealthy
1073 )
1074 {
1075 worst = health;
1076 }
1077 if matches!(worst, camel_api::HealthStatus::Degraded)
1078 && matches!(health, camel_api::HealthStatus::Unhealthy)
1079 {
1080 worst = health;
1081 }
1082 report.services.push(camel_api::ServiceHealth {
1083 name: service.name().to_string(),
1084 status: svc_status,
1085 message: None,
1086 });
1087 }
1088 report.status = worst;
1089 report
1090 }
1091
1092 async fn startup(&self) -> camel_api::HealthStatus {
1093 camel_api::HealthStatus::Healthy
1094 }
1095}
1096
1097#[cfg(test)]
1098#[path = "context_tests.rs"]
1099mod context_tests;