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::language_registry::LanguageRegistryError;
21use crate::lifecycle::adapters::controller_actor::RouteControllerHandle;
22use crate::lifecycle::adapters::route_controller::SharedLanguageRegistry;
23use crate::lifecycle::application::route_definition::RouteDefinition;
24use crate::lifecycle::application::runtime_bus::RuntimeBus;
25use crate::registry::RegistryError;
26use crate::shared::components::domain::Registry;
27use crate::shared::observability::domain::TracerConfig;
28use crate::startup_validation::ConfigCheck;
29use crate::template::TemplateRegistry;
30
31pub use crate::context_builder::CamelContextBuilder;
32
33pub struct CamelContext {
42 registry: Arc<std::sync::Mutex<Registry>>,
43 route_controller: RouteControllerHandle,
44 actor_join: Option<tokio::task::JoinHandle<()>>,
45 supervision_join: Option<tokio::task::JoinHandle<()>>,
46 runtime: Arc<RuntimeBus>,
47 cancel_token: CancellationToken,
48 metrics: Arc<dyn MetricsCollector>,
49 platform_service: Arc<dyn PlatformService>,
51 languages: SharedLanguageRegistry,
52 shutdown_timeout: std::time::Duration,
53 services: Vec<Box<dyn Lifecycle>>,
54 health_registry: Arc<HealthCheckRegistry>,
55 component_configs: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
56 function_invoker: Option<Arc<dyn FunctionInvoker>>,
57 template_registry: Arc<TemplateRegistry>,
58 idempotent_repositories: crate::registry::SharedIdempotentRegistry,
59 claim_check_repositories: crate::registry::SharedClaimCheckRegistry,
60 cache_repositories: crate::registry::SharedCacheRegistry,
61 startup_checks: Vec<Box<dyn ConfigCheck>>,
65}
66
67pub(crate) struct FromParts {
70 pub(crate) registry: Arc<std::sync::Mutex<Registry>>,
71 pub(crate) route_controller: RouteControllerHandle,
72 pub(crate) _actor_join: tokio::task::JoinHandle<()>,
73 pub(crate) supervision_join: Option<tokio::task::JoinHandle<()>>,
74 pub(crate) runtime: Arc<RuntimeBus>,
75 pub(crate) cancel_token: CancellationToken,
76 pub(crate) metrics: Arc<dyn MetricsCollector>,
77 pub(crate) platform_service: Arc<dyn PlatformService>,
78 pub(crate) languages: SharedLanguageRegistry,
79 pub(crate) shutdown_timeout: std::time::Duration,
80 pub(crate) services: Vec<Box<dyn Lifecycle>>,
81 pub(crate) health_registry: Arc<HealthCheckRegistry>,
82 pub(crate) component_configs: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
83 pub(crate) function_invoker: Option<Arc<dyn FunctionInvoker>>,
84 pub(crate) template_registry: Arc<TemplateRegistry>,
85 pub(crate) idempotent_repositories: crate::registry::SharedIdempotentRegistry,
86 pub(crate) claim_check_repositories: crate::registry::SharedClaimCheckRegistry,
87 pub(crate) cache_repositories: crate::registry::SharedCacheRegistry,
88 pub(crate) startup_checks: Vec<Box<dyn ConfigCheck>>,
89}
90
91impl CamelContext {
92 pub(crate) fn from_parts(parts: FromParts) -> Self {
93 Self {
94 registry: parts.registry,
95 route_controller: parts.route_controller,
96 actor_join: Some(parts._actor_join),
97 supervision_join: parts.supervision_join,
98 runtime: parts.runtime,
99 cancel_token: parts.cancel_token,
100 metrics: parts.metrics,
101 platform_service: parts.platform_service,
102 languages: parts.languages,
103 shutdown_timeout: parts.shutdown_timeout,
104 services: parts.services,
105 health_registry: parts.health_registry,
106 component_configs: parts.component_configs,
107 function_invoker: parts.function_invoker,
108 template_registry: parts.template_registry,
109 idempotent_repositories: parts.idempotent_repositories,
110 claim_check_repositories: parts.claim_check_repositories,
111 cache_repositories: parts.cache_repositories,
112 startup_checks: parts.startup_checks,
113 }
114 }
115}
116
117#[derive(Clone)]
121pub struct RuntimeExecutionHandle {
122 pub(crate) controller: RouteControllerHandle,
123 pub(crate) runtime: Arc<RuntimeBus>,
124 pub(crate) function_invoker: Option<Arc<dyn FunctionInvoker>>,
125 #[cfg(test)]
129 #[allow(clippy::type_complexity)]
130 pub(crate) test_lifecycle_inject: Arc<std::sync::Mutex<Option<Vec<Arc<dyn StepLifecycle>>>>>,
131}
132
133impl RuntimeExecutionHandle {
134 pub(crate) async fn add_route_definition(
135 &self,
136 definition: RouteDefinition,
137 ) -> Result<(), CamelError> {
138 use crate::lifecycle::application::ports::RouteRegistrationPort;
139 self.runtime
140 .register_route(definition)
141 .await
142 .map_err(Into::into)
143 }
144
145 #[allow(dead_code)]
148 pub(crate) async fn compile_route_definition(
149 &self,
150 definition: RouteDefinition,
151 ) -> Result<camel_api::BoxProcessor, CamelError> {
152 self.controller.compile_route_definition(definition).await
153 }
154
155 #[allow(dead_code)] pub(crate) async fn compile_route_definition_with_generation(
157 &self,
158 definition: RouteDefinition,
159 generation: u64,
160 ) -> Result<camel_api::BoxProcessor, CamelError> {
161 self.controller
162 .compile_route_definition_with_generation(definition, generation)
163 .await
164 }
165
166 pub(crate) async fn compile_route_definition_pipeline(
167 &self,
168 definition: RouteDefinition,
169 generation: u64,
170 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
171 self.controller
172 .compile_route_definition_pipeline(definition, generation)
173 .await
174 }
175
176 pub(crate) async fn compile_route_definition_dry_pipeline(
179 &self,
180 definition: RouteDefinition,
181 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
182 self.controller
183 .compile_route_definition_dry_pipeline(definition)
184 .await
185 }
186
187 pub(crate) async fn prepare_route_definition_with_generation(
188 &self,
189 definition: RouteDefinition,
190 generation: u64,
191 ) -> Result<crate::lifecycle::domain::route_compilation::PreparedRoute, CamelError> {
192 self.controller
193 .prepare_route_definition_with_generation(definition, generation)
194 .await
195 }
196
197 pub(crate) async fn insert_prepared_route(
198 &self,
199 prepared: crate::lifecycle::domain::route_compilation::PreparedRoute,
200 ) -> Result<(), CamelError> {
201 self.controller.insert_prepared_route(prepared).await
202 }
203
204 pub(crate) async fn discard_prepared_staging(&self, route_id: &str) -> Result<(), CamelError> {
205 self.controller.discard_prepared_staging(route_id).await
206 }
207
208 pub(crate) async fn remove_route_preserving_functions(
209 &self,
210 route_id: String,
211 ) -> Result<(), CamelError> {
212 self.controller
213 .remove_route_preserving_functions(route_id)
214 .await
215 }
216
217 pub(crate) async fn register_route_aggregate(
218 &self,
219 route_id: String,
220 ) -> Result<(), CamelError> {
221 self.runtime.register_aggregate_only(route_id).await
222 }
223
224 pub(crate) async fn swap_route_pipeline(
225 &self,
226 route_id: &str,
227 pipeline: camel_api::BoxProcessor,
228 ) -> Result<(), CamelError> {
229 self.controller.swap_pipeline(route_id, pipeline).await
230 }
231
232 pub(crate) async fn stop_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
234 self.controller.stop_route_reload(route_id).await
235 }
236
237 pub(crate) async fn start_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
239 self.controller.start_route_reload(route_id).await
240 }
241
242 pub(crate) async fn swap_route_pipeline_raw(
245 &self,
246 route_id: &str,
247 pipeline: camel_api::BoxProcessor,
248 lifecycle: Vec<Arc<dyn camel_api::StepLifecycle>>,
249 ) -> Result<(), CamelError> {
250 self.controller
251 .swap_pipeline_raw(route_id, pipeline, lifecycle)
252 .await
253 }
254
255 pub(crate) async fn execute_runtime_command(
256 &self,
257 cmd: camel_api::RuntimeCommand,
258 ) -> Result<camel_api::RuntimeCommandResult, CamelError> {
259 self.runtime.execute(cmd).await
260 }
261
262 pub(crate) async fn runtime_route_status(
263 &self,
264 route_id: &str,
265 ) -> Result<Option<String>, CamelError> {
266 match self
267 .runtime
268 .ask(camel_api::RuntimeQuery::GetRouteStatus {
269 route_id: route_id.to_string(),
270 })
271 .await
272 {
273 Ok(camel_api::RuntimeQueryResult::RouteStatus { status, .. }) => Ok(Some(status)),
274 Ok(_) => Err(CamelError::RouteError(
275 "unexpected runtime query response for route status".to_string(),
276 )),
277 Err(CamelError::RouteError(msg)) if msg.contains("not found") => Ok(None),
278 Err(err) => Err(err),
279 }
280 }
281
282 pub(crate) async fn runtime_route_ids(&self) -> Result<Vec<String>, CamelError> {
283 match self.runtime.ask(camel_api::RuntimeQuery::ListRoutes).await {
284 Ok(camel_api::RuntimeQueryResult::Routes { route_ids }) => Ok(route_ids),
285 Ok(_) => Err(CamelError::RouteError(
286 "unexpected runtime query response for route listing".to_string(),
287 )),
288 Err(err) => Err(err),
289 }
290 }
291
292 pub(crate) async fn route_source_hash(&self, route_id: &str) -> Option<u64> {
293 self.controller.route_source_hash(route_id).await
294 }
295
296 pub(crate) async fn in_flight_count(&self, route_id: &str) -> Result<u64, CamelError> {
297 if !self.controller.route_exists(route_id).await? {
298 return Err(CamelError::RouteError(format!(
299 "Route '{}' not found",
300 route_id
301 )));
302 }
303 Ok(self
304 .controller
305 .in_flight_count(route_id)
306 .await?
307 .unwrap_or(0))
308 }
309
310 pub(crate) async fn route_has_lifecycle(&self, route_id: &str) -> bool {
312 self.controller
313 .route_has_lifecycle(route_id)
314 .await
315 .unwrap_or(false)
316 }
317
318 pub(crate) fn function_invoker(&self) -> Option<Arc<dyn FunctionInvoker>> {
319 self.function_invoker.clone()
320 }
321
322 #[cfg(test)]
323 pub(crate) async fn force_start_route_for_test(
324 &self,
325 route_id: &str,
326 ) -> Result<(), CamelError> {
327 self.controller.start_route(route_id).await
328 }
329
330 pub async fn controller_route_count_for_test(&self) -> usize {
331 self.controller.route_count().await.unwrap_or(0)
332 }
333}
334
335#[async_trait::async_trait]
336impl crate::hot_reload::ports::ReloadExecutorPort for RuntimeExecutionHandle {
337 async fn add_route_definition(&self, definition: RouteDefinition) -> Result<(), CamelError> {
338 RuntimeExecutionHandle::add_route_definition(self, definition).await
339 }
340
341 async fn compile_route_definition_pipeline(
342 &self,
343 definition: RouteDefinition,
344 generation: u64,
345 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
346 RuntimeExecutionHandle::compile_route_definition_pipeline(self, definition, generation)
347 .await
348 }
349
350 async fn compile_route_definition_dry_pipeline(
351 &self,
352 definition: RouteDefinition,
353 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
354 RuntimeExecutionHandle::compile_route_definition_dry_pipeline(self, definition).await
355 }
356
357 async fn prepare_route_definition_with_generation(
358 &self,
359 definition: RouteDefinition,
360 generation: u64,
361 ) -> Result<crate::lifecycle::domain::route_compilation::PreparedRoute, CamelError> {
362 RuntimeExecutionHandle::prepare_route_definition_with_generation(
363 self, definition, generation,
364 )
365 .await
366 }
367
368 async fn insert_prepared_route(
369 &self,
370 prepared: crate::lifecycle::domain::route_compilation::PreparedRoute,
371 ) -> Result<(), CamelError> {
372 RuntimeExecutionHandle::insert_prepared_route(self, prepared).await
373 }
374
375 async fn discard_prepared_staging(&self, route_id: &str) -> Result<(), CamelError> {
376 RuntimeExecutionHandle::discard_prepared_staging(self, route_id).await
377 }
378
379 async fn remove_route_preserving_functions(&self, route_id: String) -> Result<(), CamelError> {
380 RuntimeExecutionHandle::remove_route_preserving_functions(self, route_id).await
381 }
382
383 async fn register_route_aggregate(&self, route_id: String) -> Result<(), CamelError> {
384 RuntimeExecutionHandle::register_route_aggregate(self, route_id).await
385 }
386
387 async fn swap_route_pipeline(
388 &self,
389 route_id: &str,
390 pipeline: camel_api::BoxProcessor,
391 ) -> Result<(), CamelError> {
392 RuntimeExecutionHandle::swap_route_pipeline(self, route_id, pipeline).await
393 }
394
395 async fn stop_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
396 RuntimeExecutionHandle::stop_route_reload(self, route_id).await
397 }
398
399 async fn start_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
400 RuntimeExecutionHandle::start_route_reload(self, route_id).await
401 }
402
403 async fn swap_route_pipeline_raw(
404 &self,
405 route_id: &str,
406 pipeline: camel_api::BoxProcessor,
407 lifecycle: Vec<std::sync::Arc<dyn camel_api::StepLifecycle>>,
408 ) -> Result<(), CamelError> {
409 RuntimeExecutionHandle::swap_route_pipeline_raw(self, route_id, pipeline, lifecycle).await
410 }
411
412 async fn execute_runtime_command(
413 &self,
414 cmd: camel_api::RuntimeCommand,
415 ) -> Result<camel_api::RuntimeCommandResult, CamelError> {
416 RuntimeExecutionHandle::execute_runtime_command(self, cmd).await
417 }
418
419 async fn runtime_route_status(&self, route_id: &str) -> Result<Option<String>, CamelError> {
420 RuntimeExecutionHandle::runtime_route_status(self, route_id).await
421 }
422
423 async fn in_flight_count(&self, route_id: &str) -> Result<u64, CamelError> {
424 RuntimeExecutionHandle::in_flight_count(self, route_id).await
425 }
426
427 async fn route_has_lifecycle(&self, route_id: &str) -> bool {
428 RuntimeExecutionHandle::route_has_lifecycle(self, route_id).await
429 }
430
431 #[cfg(test)]
432 fn take_test_lifecycle_inject(
433 &self,
434 ) -> Option<Vec<std::sync::Arc<dyn camel_api::StepLifecycle>>> {
435 self.test_lifecycle_inject.lock().unwrap().take()
436 }
437}
438
439impl CamelContext {
440 pub fn builder() -> CamelContextBuilder {
441 CamelContextBuilder::new()
442 }
443
444 pub async fn set_error_handler(&mut self, config: ErrorHandlerConfig) {
446 let _ = self.route_controller.set_error_handler(config).await;
447 }
448
449 pub async fn set_tracing(&mut self, enabled: bool) {
451 let _ = self
452 .route_controller
453 .set_tracer_config(TracerConfig {
454 enabled,
455 ..Default::default()
456 })
457 .await;
458 }
459
460 pub async fn set_tracer_config(&mut self, config: TracerConfig) {
462 let config = if config.metrics_collector.is_none() {
464 TracerConfig {
465 metrics_collector: Some(Arc::clone(&self.metrics)),
466 ..config
467 }
468 } else {
469 config
470 };
471
472 let _ = self.route_controller.set_tracer_config(config).await;
473 }
474
475 pub async fn with_tracing(mut self) -> Self {
477 self.set_tracing(true).await;
478 self
479 }
480
481 pub async fn with_tracer_config(mut self, config: TracerConfig) -> Self {
485 self.set_tracer_config(config).await;
486 self
487 }
488
489 pub fn with_lifecycle<L: Lifecycle + 'static>(mut self, service: L) -> Self {
498 if let Some(collector) = service.as_metrics_collector() {
499 self.metrics = collector;
500 }
501 if let Some(invoker) = service.as_function_invoker() {
502 self.function_invoker = Some(invoker.clone());
503 if let Err(e) = self.route_controller.try_set_function_invoker(invoker) {
504 tracing::debug!("Failed to propagate function invoker to route controller: {e}");
505 }
506 }
507
508 self.services.push(Box::new(service));
509 self
510 }
511
512 pub fn register_component<C: Component + 'static>(&mut self, component: C) {
518 self.register_component_dyn(Arc::new(component));
519 }
520
521 pub fn add_startup_check(&mut self, check: Box<dyn ConfigCheck>) {
530 self.startup_checks.push(check);
531 }
532
533 pub fn register_language(
540 &mut self,
541 name: impl Into<String>,
542 lang: Box<dyn Language>,
543 ) -> Result<(), LanguageRegistryError> {
544 let name = name.into();
545 let mut languages = self
546 .languages
547 .lock()
548 .expect("mutex poisoned: another thread panicked while holding this lock"); if languages.contains_key(&name) {
550 return Err(LanguageRegistryError::AlreadyRegistered { name });
551 }
552 languages.insert(name, Arc::from(lang));
553 Ok(())
554 }
555
556 pub fn resolve_language(&self, name: &str) -> Option<Arc<dyn Language>> {
558 let languages = self
559 .languages
560 .lock()
561 .expect("mutex poisoned: another thread panicked while holding this lock"); languages.get(name).cloned()
563 }
564
565 pub async fn add_route_definition(
569 &self,
570 definition: RouteDefinition,
571 ) -> Result<(), CamelError> {
572 use crate::lifecycle::application::ports::RouteRegistrationPort;
573 debug!(
574 from = definition.from_uri(),
575 route_id = %definition.route_id(),
576 "Adding route definition"
577 );
578 self.runtime
579 .register_route(definition)
580 .await
581 .map_err(Into::into)
582 }
583
584 pub fn registry(&self) -> std::sync::MutexGuard<'_, Registry> {
586 self.registry
587 .lock()
588 .expect("mutex poisoned: another thread panicked while holding this lock") }
590
591 pub fn registry_arc(&self) -> Arc<std::sync::Mutex<Registry>> {
593 Arc::clone(&self.registry)
594 }
595
596 pub fn runtime_execution_handle(&self) -> RuntimeExecutionHandle {
598 RuntimeExecutionHandle {
599 controller: self.route_controller.clone(),
600 runtime: Arc::clone(&self.runtime),
601 function_invoker: self.function_invoker.clone(),
602 #[cfg(test)]
603 test_lifecycle_inject: Arc::new(std::sync::Mutex::new(None)),
604 }
605 }
606
607 pub fn metrics(&self) -> Arc<dyn MetricsCollector> {
609 Arc::clone(&self.metrics)
610 }
611
612 pub fn platform_service(&self) -> Arc<dyn PlatformService> {
614 Arc::clone(&self.platform_service)
615 }
616
617 pub fn readiness_gate(&self) -> Arc<dyn ReadinessGate> {
619 self.platform_service.readiness_gate()
620 }
621
622 pub fn platform_identity(&self) -> PlatformIdentity {
624 self.platform_service.identity()
625 }
626
627 pub fn leadership(&self) -> Arc<dyn camel_api::LeadershipService> {
629 self.platform_service.leadership()
630 }
631
632 pub fn runtime(&self) -> Arc<dyn camel_api::RuntimeHandle> {
634 self.runtime.clone()
635 }
636
637 pub fn producer_context(&self) -> camel_api::ProducerContext {
639 camel_api::ProducerContext::new().with_runtime(self.runtime())
640 }
641
642 pub async fn runtime_route_status(&self, route_id: &str) -> Result<Option<String>, CamelError> {
644 match self
645 .runtime()
646 .ask(camel_api::RuntimeQuery::GetRouteStatus {
647 route_id: route_id.to_string(),
648 })
649 .await
650 {
651 Ok(camel_api::RuntimeQueryResult::RouteStatus { status, .. }) => Ok(Some(status)),
652 Ok(_) => Err(CamelError::RouteError(
653 "unexpected runtime query response for route status".to_string(),
654 )),
655 Err(CamelError::RouteError(msg)) if msg.contains("not found") => Ok(None),
656 Err(err) => Err(err),
657 }
658 }
659
660 pub async fn start(&mut self) -> Result<(), CamelError> {
668 crate::lifecycle::application::context_lifecycle::start_context(
669 &mut self.services,
670 &mut self.startup_checks,
671 &self.runtime,
672 &self.route_controller,
673 &mut self.cancel_token,
674 )
675 .await
676 }
677
678 pub async fn stop(&mut self) -> Result<(), CamelError> {
680 self.stop_timeout(self.shutdown_timeout).await
681 }
682
683 pub async fn stop_timeout(&mut self, _timeout: std::time::Duration) -> Result<(), CamelError> {
693 crate::lifecycle::application::context_lifecycle::stop_context(
694 &self.cancel_token,
695 &mut self.supervision_join,
696 &self.runtime,
697 &self.route_controller,
698 &mut self.services,
699 )
700 .await
701 }
702
703 pub fn shutdown_timeout(&self) -> std::time::Duration {
705 self.shutdown_timeout
706 }
707
708 pub fn set_shutdown_timeout(&mut self, timeout: std::time::Duration) {
710 self.shutdown_timeout = timeout;
711 }
712
713 #[cfg(test)]
716 pub(crate) fn take_actor_join(&mut self) -> Option<tokio::task::JoinHandle<()>> {
717 self.actor_join.take()
718 }
719
720 pub async fn abort(&mut self) {
728 crate::lifecycle::application::context_lifecycle::abort_context(
729 &self.cancel_token,
730 &mut self.supervision_join,
731 &self.runtime,
732 &self.route_controller as &dyn crate::lifecycle::application::ports::RouteOrderingPort,
733 &self.route_controller
734 as &dyn crate::lifecycle::application::ports::RouteDestructiveTeardownPort,
735 &mut self.services,
736 self.health_registry.cancel_token(),
737 &mut self.actor_join,
738 )
739 .await
740 }
741
742 pub async fn health_check(&self) -> HealthReport {
744 use camel_api::HealthSource;
745 self.health_report().await
746 }
747
748 pub fn health_registry(&self) -> Arc<HealthCheckRegistry> {
749 Arc::clone(&self.health_registry)
750 }
751
752 pub fn set_component_config<T: 'static + Send + Sync>(&mut self, config: T) {
754 self.component_configs
755 .insert(TypeId::of::<T>(), Box::new(config));
756 }
757
758 pub fn get_component_config<T: 'static + Send + Sync>(&self) -> Option<&T> {
760 self.component_configs
761 .get(&TypeId::of::<T>())
762 .and_then(|b| b.downcast_ref::<T>())
763 }
764
765 pub fn component_metadata(&self, scheme: &str) -> Option<ComponentMetadata> {
769 self.registry.lock().ok()?.get_metadata(scheme)
770 }
771
772 pub fn all_component_metadata(&self) -> Vec<ComponentMetadata> {
774 self.registry
775 .lock()
776 .expect("mutex poisoned: another thread panicked while holding this lock") .all_metadata()
778 }
779
780 pub fn metadata_catalog(
787 &self,
788 ) -> crate::component_metadata_catalog::RuntimeComponentMetadataCatalog {
789 crate::component_metadata_catalog::RuntimeComponentMetadataCatalog::new(Arc::clone(
790 &self.registry,
791 ))
792 }
793
794 pub fn add_route_template(&self, spec: RouteTemplateSpec) -> Result<(), CamelError> {
800 self.template_registry.register(spec)
801 }
802
803 pub fn get_route_template(&self, id: &str) -> Option<RouteTemplateSpec> {
805 self.template_registry.get(id)
806 }
807
808 pub fn template_ids(&self) -> Vec<String> {
810 self.template_registry.template_ids()
811 }
812
813 pub fn record_template_instance(&self, record: TemplateInstanceRecord) {
815 self.template_registry.record_instance(record)
816 }
817
818 pub fn template_instances(&self, template_id: &str) -> Vec<TemplateInstanceRecord> {
820 self.template_registry.instances(template_id)
821 }
822
823 pub fn register_idempotent_repository(
830 &mut self,
831 name: impl Into<String>,
832 repo: Arc<dyn camel_api::IdempotentRepository>,
833 ) -> Result<(), RegistryError> {
834 self.idempotent_repositories.register(name, repo)
835 }
836
837 pub fn idempotent_repository(
839 &self,
840 name: &str,
841 ) -> Option<Arc<dyn camel_api::IdempotentRepository>> {
842 self.idempotent_repositories.get(name)
843 }
844
845 pub fn register_claim_check_repository(
852 &mut self,
853 name: impl Into<String>,
854 repo: Arc<dyn camel_api::ClaimCheckRepository>,
855 ) -> Result<(), RegistryError> {
856 self.claim_check_repositories.register(name, repo)
857 }
858
859 pub fn claim_check_repository(
861 &self,
862 name: &str,
863 ) -> Option<Arc<dyn camel_api::ClaimCheckRepository>> {
864 self.claim_check_repositories.get(name)
865 }
866
867 pub fn register_cache_repository(
874 &mut self,
875 name: impl Into<String>,
876 repo: Arc<dyn camel_api::CacheRepository>,
877 ) -> Result<(), RegistryError> {
878 self.cache_repositories.register(name, repo)
879 }
880
881 pub fn replace_cache_repository(
885 &mut self,
886 name: impl Into<String>,
887 repo: Arc<dyn camel_api::CacheRepository>,
888 ) -> Option<Arc<dyn camel_api::CacheRepository>> {
889 self.cache_repositories.register_or_replace(name, repo)
890 }
891
892 pub fn cache_repository(&self, name: &str) -> Option<Arc<dyn camel_api::CacheRepository>> {
894 self.cache_repositories.get(name)
895 }
896
897 pub fn shutdown_token(&self) -> CancellationToken {
902 self.cancel_token.clone()
903 }
904}
905
906impl ComponentRegistrar for CamelContext {
907 fn register_component_dyn(&mut self, component: Arc<dyn Component>) {
908 let scheme = component.scheme().to_string();
909 self.registry
910 .lock()
911 .expect("mutex poisoned: another thread panicked while holding this lock") .register(component);
913 trace!(scheme, "Registered component");
914 }
915}
916
917impl ComponentContext for CamelContext {
918 fn resolve_component(&self, scheme: &str) -> Option<Arc<dyn Component>> {
919 self.registry.lock().ok()?.get(scheme)
920 }
921
922 fn resolve_language(&self, name: &str) -> Option<Arc<dyn Language>> {
923 self.languages.lock().ok()?.get(name).cloned()
924 }
925
926 fn metrics(&self) -> Arc<dyn MetricsCollector> {
927 Arc::clone(&self.metrics)
928 }
929
930 fn health(&self) -> Arc<dyn camel_component_api::HealthCheckRegistry> {
931 Arc::clone(&self.health_registry) as Arc<dyn camel_component_api::HealthCheckRegistry>
934 }
935
936 fn platform_service(&self) -> Arc<dyn PlatformService> {
937 Arc::clone(&self.platform_service)
938 }
939
940 fn register_route_health_check(
941 &self,
942 route_id: &str,
943 check: Arc<dyn camel_api::AsyncHealthCheck>,
944 ) {
945 self.health_registry.register_for_route(route_id, check);
946 }
947
948 fn unregister_route_health_check(&self, route_id: &str) {
949 self.health_registry.unregister_for_route(route_id);
950 }
951}
952
953#[async_trait::async_trait]
954impl camel_api::HealthSource for CamelContext {
955 async fn liveness(&self) -> camel_api::HealthStatus {
956 let has_failed = self
957 .services
958 .iter()
959 .any(|s| s.status() == camel_api::ServiceStatus::Failed);
960 if has_failed {
961 camel_api::HealthStatus::Unhealthy
962 } else {
963 camel_api::HealthStatus::Healthy
964 }
965 }
966
967 async fn readiness(&self) -> camel_api::HealthStatus {
968 let has_failed = self
969 .services
970 .iter()
971 .any(|s| s.status() == camel_api::ServiceStatus::Failed);
972 if has_failed {
973 return camel_api::HealthStatus::Unhealthy;
974 }
975 let has_stopped = self
976 .services
977 .iter()
978 .any(|s| s.status() == camel_api::ServiceStatus::Stopped);
979 if has_stopped {
980 return camel_api::HealthStatus::Degraded;
981 }
982 self.health_registry.check_all().await.status
983 }
984
985 async fn health_report(&self) -> camel_api::HealthReport {
986 let mut report = self.health_registry.check_all().await;
987 let mut worst = report.status;
988 for service in &self.services {
989 let svc_status = service.status();
990 let health = match svc_status {
991 camel_api::ServiceStatus::Started => camel_api::HealthStatus::Healthy,
992 camel_api::ServiceStatus::Stopped => camel_api::HealthStatus::Degraded,
993 camel_api::ServiceStatus::Failed => camel_api::HealthStatus::Unhealthy,
994 _ => camel_api::HealthStatus::Unhealthy,
997 };
998 if matches!(worst, camel_api::HealthStatus::Healthy)
999 && matches!(
1000 health,
1001 camel_api::HealthStatus::Degraded | camel_api::HealthStatus::Unhealthy
1002 )
1003 {
1004 worst = health;
1005 }
1006 if matches!(worst, camel_api::HealthStatus::Degraded)
1007 && matches!(health, camel_api::HealthStatus::Unhealthy)
1008 {
1009 worst = health;
1010 }
1011 report.services.push(camel_api::ServiceHealth {
1012 name: service.name().to_string(),
1013 status: svc_status,
1014 message: None,
1015 });
1016 }
1017 report.status = worst;
1018 report
1019 }
1020
1021 async fn startup(&self) -> camel_api::HealthStatus {
1022 camel_api::HealthStatus::Healthy
1023 }
1024}
1025
1026#[cfg(test)]
1027#[path = "context_tests.rs"]
1028mod context_tests;