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, PlatformIdentity,
13 PlatformService, ReadinessGate, RouteTemplateSpec, RuntimeCommandBus, RuntimeQueryBus,
14 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::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<dyn MetricsCollector>,
50 platform_service: Arc<dyn PlatformService>,
52 languages: SharedLanguageRegistry,
53 shutdown_timeout: std::time::Duration,
54 services: Vec<Box<dyn Lifecycle>>,
55 health_registry: Arc<HealthCheckRegistry>,
56 component_configs: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
57 function_invoker: Option<Arc<dyn FunctionInvoker>>,
58 template_registry: Arc<TemplateRegistry>,
59 idempotent_repositories: crate::registry::SharedIdempotentRegistry,
60 claim_check_repositories: crate::registry::SharedClaimCheckRegistry,
61 cache_repositories: crate::registry::SharedCacheRegistry,
62 startup_checks: Vec<Box<dyn ConfigCheck>>,
66}
67
68pub(crate) struct FromParts {
71 pub(crate) registry: Arc<std::sync::Mutex<Registry>>,
72 pub(crate) route_controller: RouteControllerHandle,
73 pub(crate) _actor_join: tokio::task::JoinHandle<()>,
74 pub(crate) supervision_join: Option<tokio::task::JoinHandle<()>>,
75 pub(crate) runtime: Arc<RuntimeBus>,
76 pub(crate) cancel_token: CancellationToken,
77 pub(crate) metrics: Arc<dyn MetricsCollector>,
78 pub(crate) platform_service: Arc<dyn PlatformService>,
79 pub(crate) languages: SharedLanguageRegistry,
80 pub(crate) shutdown_timeout: std::time::Duration,
81 pub(crate) services: Vec<Box<dyn Lifecycle>>,
82 pub(crate) health_registry: Arc<HealthCheckRegistry>,
83 pub(crate) component_configs: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
84 pub(crate) function_invoker: Option<Arc<dyn FunctionInvoker>>,
85 pub(crate) template_registry: Arc<TemplateRegistry>,
86 pub(crate) idempotent_repositories: crate::registry::SharedIdempotentRegistry,
87 pub(crate) claim_check_repositories: crate::registry::SharedClaimCheckRegistry,
88 pub(crate) cache_repositories: crate::registry::SharedCacheRegistry,
89 pub(crate) startup_checks: Vec<Box<dyn ConfigCheck>>,
90}
91
92impl CamelContext {
93 pub(crate) fn from_parts(parts: FromParts) -> Self {
94 Self {
95 registry: parts.registry,
96 route_controller: parts.route_controller,
97 actor_join: Some(parts._actor_join),
98 supervision_join: parts.supervision_join,
99 runtime: parts.runtime,
100 cancel_token: parts.cancel_token,
101 metrics: parts.metrics,
102 platform_service: parts.platform_service,
103 languages: parts.languages,
104 shutdown_timeout: parts.shutdown_timeout,
105 services: parts.services,
106 health_registry: parts.health_registry,
107 component_configs: parts.component_configs,
108 function_invoker: parts.function_invoker,
109 template_registry: parts.template_registry,
110 idempotent_repositories: parts.idempotent_repositories,
111 claim_check_repositories: parts.claim_check_repositories,
112 cache_repositories: parts.cache_repositories,
113 startup_checks: parts.startup_checks,
114 }
115 }
116}
117
118#[derive(Clone)]
122pub struct RuntimeExecutionHandle {
123 pub(crate) controller: RouteControllerHandle,
124 pub(crate) runtime: Arc<RuntimeBus>,
125 pub(crate) function_invoker: Option<Arc<dyn FunctionInvoker>>,
126 #[cfg(test)]
130 #[allow(clippy::type_complexity)]
131 pub(crate) test_lifecycle_inject: Arc<std::sync::Mutex<Option<Vec<Arc<dyn StepLifecycle>>>>>,
132}
133
134impl RuntimeExecutionHandle {
135 pub(crate) async fn add_route_definition(
136 &self,
137 definition: RouteDefinition,
138 ) -> Result<(), CamelError> {
139 use crate::lifecycle::application::ports::RouteRegistrationPort;
140 self.runtime
141 .register_route(definition)
142 .await
143 .map_err(Into::into)
144 }
145
146 #[allow(dead_code)]
149 pub(crate) async fn compile_route_definition(
150 &self,
151 definition: RouteDefinition,
152 ) -> Result<camel_api::BoxProcessor, CamelError> {
153 self.controller.compile_route_definition(definition).await
154 }
155
156 #[allow(dead_code)] pub(crate) async fn compile_route_definition_with_generation(
158 &self,
159 definition: RouteDefinition,
160 generation: u64,
161 ) -> Result<camel_api::BoxProcessor, CamelError> {
162 self.controller
163 .compile_route_definition_with_generation(definition, generation)
164 .await
165 }
166
167 pub(crate) async fn compile_route_definition_pipeline(
168 &self,
169 definition: RouteDefinition,
170 generation: u64,
171 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
172 self.controller
173 .compile_route_definition_pipeline(definition, generation)
174 .await
175 }
176
177 pub(crate) async fn compile_route_definition_dry_pipeline(
180 &self,
181 definition: RouteDefinition,
182 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
183 self.controller
184 .compile_route_definition_dry_pipeline(definition)
185 .await
186 }
187
188 pub(crate) async fn prepare_route_definition_with_generation(
189 &self,
190 definition: RouteDefinition,
191 generation: u64,
192 ) -> Result<crate::lifecycle::domain::route_compilation::PreparedRoute, CamelError> {
193 self.controller
194 .prepare_route_definition_with_generation(definition, generation)
195 .await
196 }
197
198 pub(crate) async fn insert_prepared_route(
199 &self,
200 prepared: crate::lifecycle::domain::route_compilation::PreparedRoute,
201 ) -> Result<(), CamelError> {
202 self.controller.insert_prepared_route(prepared).await
203 }
204
205 pub(crate) async fn discard_prepared_staging(&self, route_id: &str) -> Result<(), CamelError> {
206 self.controller.discard_prepared_staging(route_id).await
207 }
208
209 pub(crate) async fn remove_route_preserving_functions(
210 &self,
211 route_id: String,
212 ) -> Result<(), CamelError> {
213 self.controller
214 .remove_route_preserving_functions(route_id)
215 .await
216 }
217
218 pub(crate) async fn register_route_aggregate(
219 &self,
220 route_id: String,
221 ) -> Result<(), CamelError> {
222 self.runtime.register_aggregate_only(route_id).await
223 }
224
225 pub(crate) async fn swap_route_pipeline(
226 &self,
227 route_id: &str,
228 pipeline: camel_api::BoxProcessor,
229 ) -> Result<(), CamelError> {
230 self.controller.swap_pipeline(route_id, pipeline).await
231 }
232
233 pub(crate) async fn stop_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
235 self.controller.stop_route_reload(route_id).await
236 }
237
238 pub(crate) async fn start_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
240 self.controller.start_route_reload(route_id).await
241 }
242
243 pub(crate) async fn swap_route_pipeline_raw(
246 &self,
247 route_id: &str,
248 pipeline: camel_api::BoxProcessor,
249 lifecycle: Vec<Arc<dyn camel_api::StepLifecycle>>,
250 ) -> Result<(), CamelError> {
251 self.controller
252 .swap_pipeline_raw(route_id, pipeline, lifecycle)
253 .await
254 }
255
256 pub(crate) async fn execute_runtime_command(
257 &self,
258 cmd: camel_api::RuntimeCommand,
259 ) -> Result<camel_api::RuntimeCommandResult, CamelError> {
260 self.runtime.execute(cmd).await
261 }
262
263 pub(crate) async fn runtime_route_status(
264 &self,
265 route_id: &str,
266 ) -> Result<Option<String>, CamelError> {
267 match self
268 .runtime
269 .ask(camel_api::RuntimeQuery::GetRouteStatus {
270 route_id: route_id.to_string(),
271 })
272 .await
273 {
274 Ok(camel_api::RuntimeQueryResult::RouteStatus { status, .. }) => Ok(Some(status)),
275 Ok(_) => Err(CamelError::RouteError(
276 "unexpected runtime query response for route status".to_string(),
277 )),
278 Err(CamelError::RouteError(msg)) if msg.contains("not found") => Ok(None),
279 Err(err) => Err(err),
280 }
281 }
282
283 pub(crate) async fn runtime_route_ids(&self) -> Result<Vec<String>, CamelError> {
284 match self.runtime.ask(camel_api::RuntimeQuery::ListRoutes).await {
285 Ok(camel_api::RuntimeQueryResult::Routes { route_ids }) => Ok(route_ids),
286 Ok(_) => Err(CamelError::RouteError(
287 "unexpected runtime query response for route listing".to_string(),
288 )),
289 Err(err) => Err(err),
290 }
291 }
292
293 pub(crate) async fn route_source_hash(&self, route_id: &str) -> Option<u64> {
294 self.controller.route_source_hash(route_id).await
295 }
296
297 pub(crate) async fn in_flight_count(&self, route_id: &str) -> Result<u64, CamelError> {
298 if !self.controller.route_exists(route_id).await? {
299 return Err(CamelError::RouteError(format!(
300 "Route '{}' not found",
301 route_id
302 )));
303 }
304 Ok(self
305 .controller
306 .in_flight_count(route_id)
307 .await?
308 .unwrap_or(0))
309 }
310
311 pub(crate) async fn route_has_lifecycle(&self, route_id: &str) -> bool {
313 self.controller
314 .route_has_lifecycle(route_id)
315 .await
316 .unwrap_or(false)
317 }
318
319 pub(crate) fn function_invoker(&self) -> Option<Arc<dyn FunctionInvoker>> {
320 self.function_invoker.clone()
321 }
322
323 #[cfg(test)]
324 pub(crate) async fn force_start_route_for_test(
325 &self,
326 route_id: &str,
327 ) -> Result<(), CamelError> {
328 self.controller.start_route(route_id).await
329 }
330
331 pub async fn controller_route_count_for_test(&self) -> usize {
332 self.controller.route_count().await.unwrap_or(0)
333 }
334}
335
336#[async_trait::async_trait]
337impl crate::hot_reload::ports::ReloadExecutorPort for RuntimeExecutionHandle {
338 async fn add_route_definition(&self, definition: RouteDefinition) -> Result<(), CamelError> {
339 RuntimeExecutionHandle::add_route_definition(self, definition).await
340 }
341
342 async fn compile_route_definition_pipeline(
343 &self,
344 definition: RouteDefinition,
345 generation: u64,
346 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
347 RuntimeExecutionHandle::compile_route_definition_pipeline(self, definition, generation)
348 .await
349 }
350
351 async fn compile_route_definition_dry_pipeline(
352 &self,
353 definition: RouteDefinition,
354 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
355 RuntimeExecutionHandle::compile_route_definition_dry_pipeline(self, definition).await
356 }
357
358 async fn prepare_route_definition_with_generation(
359 &self,
360 definition: RouteDefinition,
361 generation: u64,
362 ) -> Result<crate::lifecycle::domain::route_compilation::PreparedRoute, CamelError> {
363 RuntimeExecutionHandle::prepare_route_definition_with_generation(
364 self, definition, generation,
365 )
366 .await
367 }
368
369 async fn insert_prepared_route(
370 &self,
371 prepared: crate::lifecycle::domain::route_compilation::PreparedRoute,
372 ) -> Result<(), CamelError> {
373 RuntimeExecutionHandle::insert_prepared_route(self, prepared).await
374 }
375
376 async fn discard_prepared_staging(&self, route_id: &str) -> Result<(), CamelError> {
377 RuntimeExecutionHandle::discard_prepared_staging(self, route_id).await
378 }
379
380 async fn remove_route_preserving_functions(&self, route_id: String) -> Result<(), CamelError> {
381 RuntimeExecutionHandle::remove_route_preserving_functions(self, route_id).await
382 }
383
384 async fn register_route_aggregate(&self, route_id: String) -> Result<(), CamelError> {
385 RuntimeExecutionHandle::register_route_aggregate(self, route_id).await
386 }
387
388 async fn swap_route_pipeline(
389 &self,
390 route_id: &str,
391 pipeline: camel_api::BoxProcessor,
392 ) -> Result<(), CamelError> {
393 RuntimeExecutionHandle::swap_route_pipeline(self, route_id, pipeline).await
394 }
395
396 async fn stop_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
397 RuntimeExecutionHandle::stop_route_reload(self, route_id).await
398 }
399
400 async fn start_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
401 RuntimeExecutionHandle::start_route_reload(self, route_id).await
402 }
403
404 async fn swap_route_pipeline_raw(
405 &self,
406 route_id: &str,
407 pipeline: camel_api::BoxProcessor,
408 lifecycle: Vec<std::sync::Arc<dyn camel_api::StepLifecycle>>,
409 ) -> Result<(), CamelError> {
410 RuntimeExecutionHandle::swap_route_pipeline_raw(self, route_id, pipeline, lifecycle).await
411 }
412
413 async fn execute_runtime_command(
414 &self,
415 cmd: camel_api::RuntimeCommand,
416 ) -> Result<camel_api::RuntimeCommandResult, CamelError> {
417 RuntimeExecutionHandle::execute_runtime_command(self, cmd).await
418 }
419
420 async fn runtime_route_status(&self, route_id: &str) -> Result<Option<String>, CamelError> {
421 RuntimeExecutionHandle::runtime_route_status(self, route_id).await
422 }
423
424 async fn in_flight_count(&self, route_id: &str) -> Result<u64, CamelError> {
425 RuntimeExecutionHandle::in_flight_count(self, route_id).await
426 }
427
428 async fn route_has_lifecycle(&self, route_id: &str) -> bool {
429 RuntimeExecutionHandle::route_has_lifecycle(self, route_id).await
430 }
431
432 #[cfg(test)]
433 fn take_test_lifecycle_inject(
434 &self,
435 ) -> Option<Vec<std::sync::Arc<dyn camel_api::StepLifecycle>>> {
436 self.test_lifecycle_inject.lock().unwrap().take()
437 }
438}
439
440impl CamelContext {
441 pub fn builder() -> CamelContextBuilder {
442 CamelContextBuilder::new()
443 }
444
445 pub async fn set_error_handler(&mut self, config: ErrorHandlerConfig) {
447 let _ = self.route_controller.set_error_handler(config).await;
448 }
449
450 pub async fn set_bind_exposure_acks(
454 &mut self,
455 acks: crate::lifecycle::adapters::route_controller_trait::BindExposureAcks,
456 ) {
457 let _ = self.route_controller.set_bind_exposure_acks(acks).await;
458 }
459
460 pub async fn set_tracing(&mut self, enabled: bool) {
462 let _ = self
463 .route_controller
464 .set_tracer_config(TracerConfig {
465 enabled,
466 ..Default::default()
467 })
468 .await;
469 }
470
471 pub async fn set_tracer_config(&mut self, config: TracerConfig) {
473 let config = if config.metrics_collector.is_none() {
475 TracerConfig {
476 metrics_collector: Some(Arc::clone(&self.metrics)),
477 ..config
478 }
479 } else {
480 config
481 };
482
483 let _ = self.route_controller.set_tracer_config(config).await;
484 }
485
486 pub async fn with_tracing(mut self) -> Self {
488 self.set_tracing(true).await;
489 self
490 }
491
492 pub async fn with_tracer_config(mut self, config: TracerConfig) -> Self {
496 self.set_tracer_config(config).await;
497 self
498 }
499
500 pub fn with_lifecycle<L: Lifecycle + 'static>(mut self, service: L) -> Self {
509 if let Some(collector) = service.as_metrics_collector() {
510 self.metrics = collector;
511 }
512 if let Some(invoker) = service.as_function_invoker() {
513 self.function_invoker = Some(invoker.clone());
514 if let Err(e) = self.route_controller.try_set_function_invoker(invoker) {
515 tracing::debug!("Failed to propagate function invoker to route controller: {e}");
516 }
517 }
518
519 self.services.push(Box::new(service));
520 self
521 }
522
523 pub fn register_component<C: Component + 'static>(&mut self, component: C) {
529 self.register_component_dyn(Arc::new(component));
530 }
531
532 pub async fn set_intercept_rules(&self, rules: InterceptRules) -> Result<(), CamelError> {
539 self.route_controller.set_intercept_rules(rules).await
540 }
541
542 pub fn add_startup_check(&mut self, check: Box<dyn ConfigCheck>) {
551 self.startup_checks.push(check);
552 }
553
554 pub fn register_language(
561 &mut self,
562 name: impl Into<String>,
563 lang: Box<dyn Language>,
564 ) -> Result<(), LanguageRegistryError> {
565 let name = name.into();
566 let mut languages = self
567 .languages
568 .lock()
569 .expect("mutex poisoned: another thread panicked while holding this lock"); if languages.contains_key(&name) {
571 return Err(LanguageRegistryError::AlreadyRegistered { name });
572 }
573 languages.insert(name, Arc::from(lang));
574 Ok(())
575 }
576
577 pub fn resolve_language(&self, name: &str) -> Option<Arc<dyn Language>> {
579 let languages = self
580 .languages
581 .lock()
582 .expect("mutex poisoned: another thread panicked while holding this lock"); languages.get(name).cloned()
584 }
585
586 pub async fn add_route_definition(
590 &self,
591 definition: RouteDefinition,
592 ) -> Result<(), CamelError> {
593 use crate::lifecycle::application::ports::RouteRegistrationPort;
594 debug!(
595 from = definition.from_uri(),
596 route_id = %definition.route_id(),
597 "Adding route definition"
598 );
599 self.runtime
600 .register_route(definition)
601 .await
602 .map_err(Into::into)
603 }
604
605 pub fn registry(&self) -> std::sync::MutexGuard<'_, Registry> {
607 self.registry
608 .lock()
609 .expect("mutex poisoned: another thread panicked while holding this lock") }
611
612 pub fn registry_arc(&self) -> Arc<std::sync::Mutex<Registry>> {
614 Arc::clone(&self.registry)
615 }
616
617 pub fn runtime_execution_handle(&self) -> RuntimeExecutionHandle {
619 RuntimeExecutionHandle {
620 controller: self.route_controller.clone(),
621 runtime: Arc::clone(&self.runtime),
622 function_invoker: self.function_invoker.clone(),
623 #[cfg(test)]
624 test_lifecycle_inject: Arc::new(std::sync::Mutex::new(None)),
625 }
626 }
627
628 pub fn metrics(&self) -> Arc<dyn MetricsCollector> {
630 Arc::clone(&self.metrics)
631 }
632
633 pub fn platform_service(&self) -> Arc<dyn PlatformService> {
635 Arc::clone(&self.platform_service)
636 }
637
638 pub fn readiness_gate(&self) -> Arc<dyn ReadinessGate> {
640 self.platform_service.readiness_gate()
641 }
642
643 pub fn platform_identity(&self) -> PlatformIdentity {
645 self.platform_service.identity()
646 }
647
648 pub fn leadership(&self) -> Arc<dyn camel_api::LeadershipService> {
650 self.platform_service.leadership()
651 }
652
653 pub fn runtime(&self) -> Arc<dyn camel_api::RuntimeHandle> {
655 self.runtime.clone()
656 }
657
658 pub fn producer_context(&self) -> camel_api::ProducerContext {
660 camel_api::ProducerContext::new().with_runtime(self.runtime())
661 }
662
663 pub async fn runtime_route_status(&self, route_id: &str) -> Result<Option<String>, CamelError> {
665 match self
666 .runtime()
667 .ask(camel_api::RuntimeQuery::GetRouteStatus {
668 route_id: route_id.to_string(),
669 })
670 .await
671 {
672 Ok(camel_api::RuntimeQueryResult::RouteStatus { status, .. }) => Ok(Some(status)),
673 Ok(_) => Err(CamelError::RouteError(
674 "unexpected runtime query response for route status".to_string(),
675 )),
676 Err(CamelError::RouteError(msg)) if msg.contains("not found") => Ok(None),
677 Err(err) => Err(err),
678 }
679 }
680
681 pub async fn start(&mut self) -> Result<(), CamelError> {
689 crate::lifecycle::application::context_lifecycle::start_context(
690 &mut self.services,
691 &mut self.startup_checks,
692 &self.runtime,
693 &self.route_controller,
694 &mut self.cancel_token,
695 )
696 .await?;
697 self.route_controller.mark_started().await
701 }
702
703 pub async fn stop(&mut self) -> Result<(), CamelError> {
705 self.stop_timeout(self.shutdown_timeout).await
706 }
707
708 pub async fn stop_timeout(&mut self, _timeout: std::time::Duration) -> Result<(), CamelError> {
718 crate::lifecycle::application::context_lifecycle::stop_context(
719 &self.cancel_token,
720 &mut self.supervision_join,
721 &self.runtime,
722 &self.route_controller,
723 &mut self.services,
724 )
725 .await
726 }
727
728 pub fn shutdown_timeout(&self) -> std::time::Duration {
730 self.shutdown_timeout
731 }
732
733 pub fn set_shutdown_timeout(&mut self, timeout: std::time::Duration) {
735 self.shutdown_timeout = timeout;
736 }
737
738 #[cfg(test)]
741 pub(crate) fn take_actor_join(&mut self) -> Option<tokio::task::JoinHandle<()>> {
742 self.actor_join.take()
743 }
744
745 pub async fn abort(&mut self) {
753 crate::lifecycle::application::context_lifecycle::abort_context(
754 &self.cancel_token,
755 &mut self.supervision_join,
756 &self.runtime,
757 &self.route_controller as &dyn crate::lifecycle::application::ports::RouteOrderingPort,
758 &self.route_controller
759 as &dyn crate::lifecycle::application::ports::RouteDestructiveTeardownPort,
760 &mut self.services,
761 self.health_registry.cancel_token(),
762 &mut self.actor_join,
763 )
764 .await
765 }
766
767 pub async fn health_check(&self) -> HealthReport {
769 use camel_api::HealthSource;
770 self.health_report().await
771 }
772
773 pub fn health_registry(&self) -> Arc<HealthCheckRegistry> {
774 Arc::clone(&self.health_registry)
775 }
776
777 pub fn set_component_config<T: 'static + Send + Sync>(&mut self, config: T) {
779 self.component_configs
780 .insert(TypeId::of::<T>(), Box::new(config));
781 }
782
783 pub fn get_component_config<T: 'static + Send + Sync>(&self) -> Option<&T> {
785 self.component_configs
786 .get(&TypeId::of::<T>())
787 .and_then(|b| b.downcast_ref::<T>())
788 }
789
790 pub fn component_metadata(&self, scheme: &str) -> Option<ComponentMetadata> {
794 self.registry.lock().ok()?.get_metadata(scheme)
795 }
796
797 pub fn all_component_metadata(&self) -> Vec<ComponentMetadata> {
799 self.registry
800 .lock()
801 .expect("mutex poisoned: another thread panicked while holding this lock") .all_metadata()
803 }
804
805 pub fn metadata_catalog(
812 &self,
813 ) -> crate::component_metadata_catalog::RuntimeComponentMetadataCatalog {
814 crate::component_metadata_catalog::RuntimeComponentMetadataCatalog::new(Arc::clone(
815 &self.registry,
816 ))
817 }
818
819 pub fn add_route_template(&self, spec: RouteTemplateSpec) -> Result<(), CamelError> {
825 self.template_registry.register(spec)
826 }
827
828 pub fn get_route_template(&self, id: &str) -> Option<RouteTemplateSpec> {
830 self.template_registry.get(id)
831 }
832
833 pub fn template_ids(&self) -> Vec<String> {
835 self.template_registry.template_ids()
836 }
837
838 pub fn record_template_instance(&self, record: TemplateInstanceRecord) {
840 self.template_registry.record_instance(record)
841 }
842
843 pub fn template_instances(&self, template_id: &str) -> Vec<TemplateInstanceRecord> {
845 self.template_registry.instances(template_id)
846 }
847
848 pub fn register_idempotent_repository(
855 &mut self,
856 name: impl Into<String>,
857 repo: Arc<dyn camel_api::IdempotentRepository>,
858 ) -> Result<(), RegistryError> {
859 self.idempotent_repositories.register(name, repo)
860 }
861
862 pub fn idempotent_repository(
864 &self,
865 name: &str,
866 ) -> Option<Arc<dyn camel_api::IdempotentRepository>> {
867 self.idempotent_repositories.get(name)
868 }
869
870 pub fn register_claim_check_repository(
877 &mut self,
878 name: impl Into<String>,
879 repo: Arc<dyn camel_api::ClaimCheckRepository>,
880 ) -> Result<(), RegistryError> {
881 self.claim_check_repositories.register(name, repo)
882 }
883
884 pub fn claim_check_repository(
886 &self,
887 name: &str,
888 ) -> Option<Arc<dyn camel_api::ClaimCheckRepository>> {
889 self.claim_check_repositories.get(name)
890 }
891
892 pub fn register_cache_repository(
899 &mut self,
900 name: impl Into<String>,
901 repo: Arc<dyn camel_api::CacheRepository>,
902 ) -> Result<(), RegistryError> {
903 self.cache_repositories.register(name, repo)
904 }
905
906 pub fn replace_cache_repository(
910 &mut self,
911 name: impl Into<String>,
912 repo: Arc<dyn camel_api::CacheRepository>,
913 ) -> Option<Arc<dyn camel_api::CacheRepository>> {
914 self.cache_repositories.register_or_replace(name, repo)
915 }
916
917 pub fn cache_repository(&self, name: &str) -> Option<Arc<dyn camel_api::CacheRepository>> {
919 self.cache_repositories.get(name)
920 }
921
922 pub fn shutdown_token(&self) -> CancellationToken {
927 self.cancel_token.clone()
928 }
929}
930
931impl ComponentRegistrar for CamelContext {
932 fn register_component_dyn(&mut self, component: Arc<dyn Component>) {
933 let scheme = component.scheme().to_string();
934 self.registry
935 .lock()
936 .expect("mutex poisoned: another thread panicked while holding this lock") .register(component);
938 trace!(scheme, "Registered component");
939 }
940}
941
942impl ComponentContext for CamelContext {
943 fn resolve_component(&self, scheme: &str) -> Option<Arc<dyn Component>> {
944 self.registry.lock().ok()?.get(scheme)
945 }
946
947 fn resolve_language(&self, name: &str) -> Option<Arc<dyn Language>> {
948 self.languages.lock().ok()?.get(name).cloned()
949 }
950
951 fn metrics(&self) -> Arc<dyn MetricsCollector> {
952 Arc::clone(&self.metrics)
953 }
954
955 fn health(&self) -> Arc<dyn camel_component_api::HealthCheckRegistry> {
956 Arc::clone(&self.health_registry) as Arc<dyn camel_component_api::HealthCheckRegistry>
959 }
960
961 fn platform_service(&self) -> Arc<dyn PlatformService> {
962 Arc::clone(&self.platform_service)
963 }
964
965 fn register_route_health_check(
966 &self,
967 route_id: &str,
968 check: Arc<dyn camel_api::AsyncHealthCheck>,
969 ) {
970 self.health_registry.register_for_route(route_id, check);
971 }
972
973 fn unregister_route_health_check(&self, route_id: &str) {
974 self.health_registry.unregister_for_route(route_id);
975 }
976}
977
978#[async_trait::async_trait]
979impl camel_api::HealthSource for CamelContext {
980 async fn liveness(&self) -> camel_api::HealthStatus {
981 let has_failed = self
982 .services
983 .iter()
984 .any(|s| s.status() == camel_api::ServiceStatus::Failed);
985 if has_failed {
986 camel_api::HealthStatus::Unhealthy
987 } else {
988 camel_api::HealthStatus::Healthy
989 }
990 }
991
992 async fn readiness(&self) -> camel_api::HealthStatus {
993 let has_failed = self
994 .services
995 .iter()
996 .any(|s| s.status() == camel_api::ServiceStatus::Failed);
997 if has_failed {
998 return camel_api::HealthStatus::Unhealthy;
999 }
1000 let has_stopped = self
1001 .services
1002 .iter()
1003 .any(|s| s.status() == camel_api::ServiceStatus::Stopped);
1004 if has_stopped {
1005 return camel_api::HealthStatus::Degraded;
1006 }
1007 self.health_registry.check_all().await.status
1008 }
1009
1010 async fn health_report(&self) -> camel_api::HealthReport {
1011 let mut report = self.health_registry.check_all().await;
1012 let mut worst = report.status;
1013 for service in &self.services {
1014 let svc_status = service.status();
1015 let health = match svc_status {
1016 camel_api::ServiceStatus::Started => camel_api::HealthStatus::Healthy,
1017 camel_api::ServiceStatus::Stopped => camel_api::HealthStatus::Degraded,
1018 camel_api::ServiceStatus::Failed => camel_api::HealthStatus::Unhealthy,
1019 _ => camel_api::HealthStatus::Unhealthy,
1022 };
1023 if matches!(worst, camel_api::HealthStatus::Healthy)
1024 && matches!(
1025 health,
1026 camel_api::HealthStatus::Degraded | camel_api::HealthStatus::Unhealthy
1027 )
1028 {
1029 worst = health;
1030 }
1031 if matches!(worst, camel_api::HealthStatus::Degraded)
1032 && matches!(health, camel_api::HealthStatus::Unhealthy)
1033 {
1034 worst = health;
1035 }
1036 report.services.push(camel_api::ServiceHealth {
1037 name: service.name().to_string(),
1038 status: svc_status,
1039 message: None,
1040 });
1041 }
1042 report.status = worst;
1043 report
1044 }
1045
1046 async fn startup(&self) -> camel_api::HealthStatus {
1047 camel_api::HealthStatus::Healthy
1048 }
1049}
1050
1051#[cfg(test)]
1052#[path = "context_tests.rs"]
1053mod context_tests;