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 startup_checks: Vec<Box<dyn ConfigCheck>>,
64}
65
66pub(crate) struct FromParts {
69 pub(crate) registry: Arc<std::sync::Mutex<Registry>>,
70 pub(crate) route_controller: RouteControllerHandle,
71 pub(crate) _actor_join: tokio::task::JoinHandle<()>,
72 pub(crate) supervision_join: Option<tokio::task::JoinHandle<()>>,
73 pub(crate) runtime: Arc<RuntimeBus>,
74 pub(crate) cancel_token: CancellationToken,
75 pub(crate) metrics: Arc<dyn MetricsCollector>,
76 pub(crate) platform_service: Arc<dyn PlatformService>,
77 pub(crate) languages: SharedLanguageRegistry,
78 pub(crate) shutdown_timeout: std::time::Duration,
79 pub(crate) services: Vec<Box<dyn Lifecycle>>,
80 pub(crate) health_registry: Arc<HealthCheckRegistry>,
81 pub(crate) component_configs: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
82 pub(crate) function_invoker: Option<Arc<dyn FunctionInvoker>>,
83 pub(crate) template_registry: Arc<TemplateRegistry>,
84 pub(crate) idempotent_repositories: crate::registry::SharedIdempotentRegistry,
85 pub(crate) claim_check_repositories: crate::registry::SharedClaimCheckRegistry,
86 pub(crate) startup_checks: Vec<Box<dyn ConfigCheck>>,
87}
88
89impl CamelContext {
90 pub(crate) fn from_parts(parts: FromParts) -> Self {
91 Self {
92 registry: parts.registry,
93 route_controller: parts.route_controller,
94 actor_join: Some(parts._actor_join),
95 supervision_join: parts.supervision_join,
96 runtime: parts.runtime,
97 cancel_token: parts.cancel_token,
98 metrics: parts.metrics,
99 platform_service: parts.platform_service,
100 languages: parts.languages,
101 shutdown_timeout: parts.shutdown_timeout,
102 services: parts.services,
103 health_registry: parts.health_registry,
104 component_configs: parts.component_configs,
105 function_invoker: parts.function_invoker,
106 template_registry: parts.template_registry,
107 idempotent_repositories: parts.idempotent_repositories,
108 claim_check_repositories: parts.claim_check_repositories,
109 startup_checks: parts.startup_checks,
110 }
111 }
112}
113
114#[derive(Clone)]
118pub struct RuntimeExecutionHandle {
119 pub(crate) controller: RouteControllerHandle,
120 pub(crate) runtime: Arc<RuntimeBus>,
121 pub(crate) function_invoker: Option<Arc<dyn FunctionInvoker>>,
122 #[cfg(test)]
126 #[allow(clippy::type_complexity)]
127 pub(crate) test_lifecycle_inject: Arc<std::sync::Mutex<Option<Vec<Arc<dyn StepLifecycle>>>>>,
128}
129
130impl RuntimeExecutionHandle {
131 pub(crate) async fn add_route_definition(
132 &self,
133 definition: RouteDefinition,
134 ) -> Result<(), CamelError> {
135 use crate::lifecycle::application::ports::RouteRegistrationPort;
136 self.runtime
137 .register_route(definition)
138 .await
139 .map_err(Into::into)
140 }
141
142 #[allow(dead_code)]
145 pub(crate) async fn compile_route_definition(
146 &self,
147 definition: RouteDefinition,
148 ) -> Result<camel_api::BoxProcessor, CamelError> {
149 self.controller.compile_route_definition(definition).await
150 }
151
152 #[allow(dead_code)] pub(crate) async fn compile_route_definition_with_generation(
154 &self,
155 definition: RouteDefinition,
156 generation: u64,
157 ) -> Result<camel_api::BoxProcessor, CamelError> {
158 self.controller
159 .compile_route_definition_with_generation(definition, generation)
160 .await
161 }
162
163 pub(crate) async fn compile_route_definition_pipeline(
164 &self,
165 definition: RouteDefinition,
166 generation: u64,
167 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
168 self.controller
169 .compile_route_definition_pipeline(definition, generation)
170 .await
171 }
172
173 pub(crate) async fn compile_route_definition_dry_pipeline(
176 &self,
177 definition: RouteDefinition,
178 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
179 self.controller
180 .compile_route_definition_dry_pipeline(definition)
181 .await
182 }
183
184 pub(crate) async fn prepare_route_definition_with_generation(
185 &self,
186 definition: RouteDefinition,
187 generation: u64,
188 ) -> Result<crate::lifecycle::domain::route_compilation::PreparedRoute, CamelError> {
189 self.controller
190 .prepare_route_definition_with_generation(definition, generation)
191 .await
192 }
193
194 pub(crate) async fn insert_prepared_route(
195 &self,
196 prepared: crate::lifecycle::domain::route_compilation::PreparedRoute,
197 ) -> Result<(), CamelError> {
198 self.controller.insert_prepared_route(prepared).await
199 }
200
201 pub(crate) async fn discard_prepared_staging(&self, route_id: &str) -> Result<(), CamelError> {
202 self.controller.discard_prepared_staging(route_id).await
203 }
204
205 pub(crate) async fn remove_route_preserving_functions(
206 &self,
207 route_id: String,
208 ) -> Result<(), CamelError> {
209 self.controller
210 .remove_route_preserving_functions(route_id)
211 .await
212 }
213
214 pub(crate) async fn register_route_aggregate(
215 &self,
216 route_id: String,
217 ) -> Result<(), CamelError> {
218 self.runtime.register_aggregate_only(route_id).await
219 }
220
221 pub(crate) async fn swap_route_pipeline(
222 &self,
223 route_id: &str,
224 pipeline: camel_api::BoxProcessor,
225 ) -> Result<(), CamelError> {
226 self.controller.swap_pipeline(route_id, pipeline).await
227 }
228
229 pub(crate) async fn stop_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
231 self.controller.stop_route_reload(route_id).await
232 }
233
234 pub(crate) async fn start_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
236 self.controller.start_route_reload(route_id).await
237 }
238
239 pub(crate) async fn swap_route_pipeline_raw(
242 &self,
243 route_id: &str,
244 pipeline: camel_api::BoxProcessor,
245 lifecycle: Vec<Arc<dyn camel_api::StepLifecycle>>,
246 ) -> Result<(), CamelError> {
247 self.controller
248 .swap_pipeline_raw(route_id, pipeline, lifecycle)
249 .await
250 }
251
252 pub(crate) async fn execute_runtime_command(
253 &self,
254 cmd: camel_api::RuntimeCommand,
255 ) -> Result<camel_api::RuntimeCommandResult, CamelError> {
256 self.runtime.execute(cmd).await
257 }
258
259 pub(crate) async fn runtime_route_status(
260 &self,
261 route_id: &str,
262 ) -> Result<Option<String>, CamelError> {
263 match self
264 .runtime
265 .ask(camel_api::RuntimeQuery::GetRouteStatus {
266 route_id: route_id.to_string(),
267 })
268 .await
269 {
270 Ok(camel_api::RuntimeQueryResult::RouteStatus { status, .. }) => Ok(Some(status)),
271 Ok(_) => Err(CamelError::RouteError(
272 "unexpected runtime query response for route status".to_string(),
273 )),
274 Err(CamelError::RouteError(msg)) if msg.contains("not found") => Ok(None),
275 Err(err) => Err(err),
276 }
277 }
278
279 pub(crate) async fn runtime_route_ids(&self) -> Result<Vec<String>, CamelError> {
280 match self.runtime.ask(camel_api::RuntimeQuery::ListRoutes).await {
281 Ok(camel_api::RuntimeQueryResult::Routes { route_ids }) => Ok(route_ids),
282 Ok(_) => Err(CamelError::RouteError(
283 "unexpected runtime query response for route listing".to_string(),
284 )),
285 Err(err) => Err(err),
286 }
287 }
288
289 pub(crate) async fn route_source_hash(&self, route_id: &str) -> Option<u64> {
290 self.controller.route_source_hash(route_id).await
291 }
292
293 pub(crate) async fn in_flight_count(&self, route_id: &str) -> Result<u64, CamelError> {
294 if !self.controller.route_exists(route_id).await? {
295 return Err(CamelError::RouteError(format!(
296 "Route '{}' not found",
297 route_id
298 )));
299 }
300 Ok(self
301 .controller
302 .in_flight_count(route_id)
303 .await?
304 .unwrap_or(0))
305 }
306
307 pub(crate) async fn route_has_lifecycle(&self, route_id: &str) -> bool {
309 self.controller
310 .route_has_lifecycle(route_id)
311 .await
312 .unwrap_or(false)
313 }
314
315 pub(crate) fn function_invoker(&self) -> Option<Arc<dyn FunctionInvoker>> {
316 self.function_invoker.clone()
317 }
318
319 #[cfg(test)]
320 pub(crate) async fn force_start_route_for_test(
321 &self,
322 route_id: &str,
323 ) -> Result<(), CamelError> {
324 self.controller.start_route(route_id).await
325 }
326
327 pub async fn controller_route_count_for_test(&self) -> usize {
328 self.controller.route_count().await.unwrap_or(0)
329 }
330}
331
332#[async_trait::async_trait]
333impl crate::hot_reload::ports::ReloadExecutorPort for RuntimeExecutionHandle {
334 async fn add_route_definition(&self, definition: RouteDefinition) -> Result<(), CamelError> {
335 RuntimeExecutionHandle::add_route_definition(self, definition).await
336 }
337
338 async fn compile_route_definition_pipeline(
339 &self,
340 definition: RouteDefinition,
341 generation: u64,
342 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
343 RuntimeExecutionHandle::compile_route_definition_pipeline(self, definition, generation)
344 .await
345 }
346
347 async fn compile_route_definition_dry_pipeline(
348 &self,
349 definition: RouteDefinition,
350 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
351 RuntimeExecutionHandle::compile_route_definition_dry_pipeline(self, definition).await
352 }
353
354 async fn prepare_route_definition_with_generation(
355 &self,
356 definition: RouteDefinition,
357 generation: u64,
358 ) -> Result<crate::lifecycle::domain::route_compilation::PreparedRoute, CamelError> {
359 RuntimeExecutionHandle::prepare_route_definition_with_generation(
360 self, definition, generation,
361 )
362 .await
363 }
364
365 async fn insert_prepared_route(
366 &self,
367 prepared: crate::lifecycle::domain::route_compilation::PreparedRoute,
368 ) -> Result<(), CamelError> {
369 RuntimeExecutionHandle::insert_prepared_route(self, prepared).await
370 }
371
372 async fn discard_prepared_staging(&self, route_id: &str) -> Result<(), CamelError> {
373 RuntimeExecutionHandle::discard_prepared_staging(self, route_id).await
374 }
375
376 async fn remove_route_preserving_functions(&self, route_id: String) -> Result<(), CamelError> {
377 RuntimeExecutionHandle::remove_route_preserving_functions(self, route_id).await
378 }
379
380 async fn register_route_aggregate(&self, route_id: String) -> Result<(), CamelError> {
381 RuntimeExecutionHandle::register_route_aggregate(self, route_id).await
382 }
383
384 async fn swap_route_pipeline(
385 &self,
386 route_id: &str,
387 pipeline: camel_api::BoxProcessor,
388 ) -> Result<(), CamelError> {
389 RuntimeExecutionHandle::swap_route_pipeline(self, route_id, pipeline).await
390 }
391
392 async fn stop_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
393 RuntimeExecutionHandle::stop_route_reload(self, route_id).await
394 }
395
396 async fn start_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
397 RuntimeExecutionHandle::start_route_reload(self, route_id).await
398 }
399
400 async fn swap_route_pipeline_raw(
401 &self,
402 route_id: &str,
403 pipeline: camel_api::BoxProcessor,
404 lifecycle: Vec<std::sync::Arc<dyn camel_api::StepLifecycle>>,
405 ) -> Result<(), CamelError> {
406 RuntimeExecutionHandle::swap_route_pipeline_raw(self, route_id, pipeline, lifecycle).await
407 }
408
409 async fn execute_runtime_command(
410 &self,
411 cmd: camel_api::RuntimeCommand,
412 ) -> Result<camel_api::RuntimeCommandResult, CamelError> {
413 RuntimeExecutionHandle::execute_runtime_command(self, cmd).await
414 }
415
416 async fn runtime_route_status(&self, route_id: &str) -> Result<Option<String>, CamelError> {
417 RuntimeExecutionHandle::runtime_route_status(self, route_id).await
418 }
419
420 async fn in_flight_count(&self, route_id: &str) -> Result<u64, CamelError> {
421 RuntimeExecutionHandle::in_flight_count(self, route_id).await
422 }
423
424 async fn route_has_lifecycle(&self, route_id: &str) -> bool {
425 RuntimeExecutionHandle::route_has_lifecycle(self, route_id).await
426 }
427
428 #[cfg(test)]
429 fn take_test_lifecycle_inject(
430 &self,
431 ) -> Option<Vec<std::sync::Arc<dyn camel_api::StepLifecycle>>> {
432 self.test_lifecycle_inject.lock().unwrap().take()
433 }
434}
435
436impl CamelContext {
437 pub fn builder() -> CamelContextBuilder {
438 CamelContextBuilder::new()
439 }
440
441 pub async fn set_error_handler(&mut self, config: ErrorHandlerConfig) {
443 let _ = self.route_controller.set_error_handler(config).await;
444 }
445
446 pub async fn set_tracing(&mut self, enabled: bool) {
448 let _ = self
449 .route_controller
450 .set_tracer_config(TracerConfig {
451 enabled,
452 ..Default::default()
453 })
454 .await;
455 }
456
457 pub async fn set_tracer_config(&mut self, config: TracerConfig) {
459 let config = if config.metrics_collector.is_none() {
461 TracerConfig {
462 metrics_collector: Some(Arc::clone(&self.metrics)),
463 ..config
464 }
465 } else {
466 config
467 };
468
469 let _ = self.route_controller.set_tracer_config(config).await;
470 }
471
472 pub async fn with_tracing(mut self) -> Self {
474 self.set_tracing(true).await;
475 self
476 }
477
478 pub async fn with_tracer_config(mut self, config: TracerConfig) -> Self {
482 self.set_tracer_config(config).await;
483 self
484 }
485
486 pub fn with_lifecycle<L: Lifecycle + 'static>(mut self, service: L) -> Self {
495 if let Some(collector) = service.as_metrics_collector() {
496 self.metrics = collector;
497 }
498 if let Some(invoker) = service.as_function_invoker() {
499 self.function_invoker = Some(invoker.clone());
500 if let Err(e) = self.route_controller.try_set_function_invoker(invoker) {
501 tracing::debug!("Failed to propagate function invoker to route controller: {e}");
502 }
503 }
504
505 self.services.push(Box::new(service));
506 self
507 }
508
509 pub fn register_component<C: Component + 'static>(&mut self, component: C) {
515 self.register_component_dyn(Arc::new(component));
516 }
517
518 pub fn add_startup_check(&mut self, check: Box<dyn ConfigCheck>) {
527 self.startup_checks.push(check);
528 }
529
530 pub fn register_language(
537 &mut self,
538 name: impl Into<String>,
539 lang: Box<dyn Language>,
540 ) -> Result<(), LanguageRegistryError> {
541 let name = name.into();
542 let mut languages = self
543 .languages
544 .lock()
545 .expect("mutex poisoned: another thread panicked while holding this lock"); if languages.contains_key(&name) {
547 return Err(LanguageRegistryError::AlreadyRegistered { name });
548 }
549 languages.insert(name, Arc::from(lang));
550 Ok(())
551 }
552
553 pub fn resolve_language(&self, name: &str) -> Option<Arc<dyn Language>> {
555 let languages = self
556 .languages
557 .lock()
558 .expect("mutex poisoned: another thread panicked while holding this lock"); languages.get(name).cloned()
560 }
561
562 pub async fn add_route_definition(
566 &self,
567 definition: RouteDefinition,
568 ) -> Result<(), CamelError> {
569 use crate::lifecycle::application::ports::RouteRegistrationPort;
570 debug!(
571 from = definition.from_uri(),
572 route_id = %definition.route_id(),
573 "Adding route definition"
574 );
575 self.runtime
576 .register_route(definition)
577 .await
578 .map_err(Into::into)
579 }
580
581 pub fn registry(&self) -> std::sync::MutexGuard<'_, Registry> {
583 self.registry
584 .lock()
585 .expect("mutex poisoned: another thread panicked while holding this lock") }
587
588 pub fn registry_arc(&self) -> Arc<std::sync::Mutex<Registry>> {
590 Arc::clone(&self.registry)
591 }
592
593 pub fn runtime_execution_handle(&self) -> RuntimeExecutionHandle {
595 RuntimeExecutionHandle {
596 controller: self.route_controller.clone(),
597 runtime: Arc::clone(&self.runtime),
598 function_invoker: self.function_invoker.clone(),
599 #[cfg(test)]
600 test_lifecycle_inject: Arc::new(std::sync::Mutex::new(None)),
601 }
602 }
603
604 pub fn metrics(&self) -> Arc<dyn MetricsCollector> {
606 Arc::clone(&self.metrics)
607 }
608
609 pub fn platform_service(&self) -> Arc<dyn PlatformService> {
611 Arc::clone(&self.platform_service)
612 }
613
614 pub fn readiness_gate(&self) -> Arc<dyn ReadinessGate> {
616 self.platform_service.readiness_gate()
617 }
618
619 pub fn platform_identity(&self) -> PlatformIdentity {
621 self.platform_service.identity()
622 }
623
624 pub fn leadership(&self) -> Arc<dyn camel_api::LeadershipService> {
626 self.platform_service.leadership()
627 }
628
629 pub fn runtime(&self) -> Arc<dyn camel_api::RuntimeHandle> {
631 self.runtime.clone()
632 }
633
634 pub fn producer_context(&self) -> camel_api::ProducerContext {
636 camel_api::ProducerContext::new().with_runtime(self.runtime())
637 }
638
639 pub async fn runtime_route_status(&self, route_id: &str) -> Result<Option<String>, CamelError> {
641 match self
642 .runtime()
643 .ask(camel_api::RuntimeQuery::GetRouteStatus {
644 route_id: route_id.to_string(),
645 })
646 .await
647 {
648 Ok(camel_api::RuntimeQueryResult::RouteStatus { status, .. }) => Ok(Some(status)),
649 Ok(_) => Err(CamelError::RouteError(
650 "unexpected runtime query response for route status".to_string(),
651 )),
652 Err(CamelError::RouteError(msg)) if msg.contains("not found") => Ok(None),
653 Err(err) => Err(err),
654 }
655 }
656
657 pub async fn start(&mut self) -> Result<(), CamelError> {
665 crate::lifecycle::application::context_lifecycle::start_context(
666 &mut self.services,
667 &mut self.startup_checks,
668 &self.runtime,
669 &self.route_controller,
670 &mut self.cancel_token,
671 )
672 .await
673 }
674
675 pub async fn stop(&mut self) -> Result<(), CamelError> {
677 self.stop_timeout(self.shutdown_timeout).await
678 }
679
680 pub async fn stop_timeout(&mut self, _timeout: std::time::Duration) -> Result<(), CamelError> {
690 crate::lifecycle::application::context_lifecycle::stop_context(
691 &self.cancel_token,
692 &mut self.supervision_join,
693 &self.runtime,
694 &self.route_controller,
695 &mut self.services,
696 )
697 .await
698 }
699
700 pub fn shutdown_timeout(&self) -> std::time::Duration {
702 self.shutdown_timeout
703 }
704
705 pub fn set_shutdown_timeout(&mut self, timeout: std::time::Duration) {
707 self.shutdown_timeout = timeout;
708 }
709
710 #[cfg(test)]
713 pub(crate) fn take_actor_join(&mut self) -> Option<tokio::task::JoinHandle<()>> {
714 self.actor_join.take()
715 }
716
717 pub async fn abort(&mut self) {
725 crate::lifecycle::application::context_lifecycle::abort_context(
726 &self.cancel_token,
727 &mut self.supervision_join,
728 &self.runtime,
729 &self.route_controller as &dyn crate::lifecycle::application::ports::RouteOrderingPort,
730 &self.route_controller
731 as &dyn crate::lifecycle::application::ports::RouteDestructiveTeardownPort,
732 &mut self.services,
733 self.health_registry.cancel_token(),
734 &mut self.actor_join,
735 )
736 .await
737 }
738
739 pub async fn health_check(&self) -> HealthReport {
741 use camel_api::HealthSource;
742 self.health_report().await
743 }
744
745 pub fn health_registry(&self) -> Arc<HealthCheckRegistry> {
746 Arc::clone(&self.health_registry)
747 }
748
749 pub fn set_component_config<T: 'static + Send + Sync>(&mut self, config: T) {
751 self.component_configs
752 .insert(TypeId::of::<T>(), Box::new(config));
753 }
754
755 pub fn get_component_config<T: 'static + Send + Sync>(&self) -> Option<&T> {
757 self.component_configs
758 .get(&TypeId::of::<T>())
759 .and_then(|b| b.downcast_ref::<T>())
760 }
761
762 pub fn component_metadata(&self, scheme: &str) -> Option<ComponentMetadata> {
766 self.registry.lock().ok()?.get_metadata(scheme)
767 }
768
769 pub fn all_component_metadata(&self) -> Vec<ComponentMetadata> {
771 self.registry
772 .lock()
773 .expect("mutex poisoned: another thread panicked while holding this lock") .all_metadata()
775 }
776
777 pub fn metadata_catalog(
784 &self,
785 ) -> crate::component_metadata_catalog::RuntimeComponentMetadataCatalog {
786 crate::component_metadata_catalog::RuntimeComponentMetadataCatalog::new(Arc::clone(
787 &self.registry,
788 ))
789 }
790
791 pub fn add_route_template(&self, spec: RouteTemplateSpec) -> Result<(), CamelError> {
797 self.template_registry.register(spec)
798 }
799
800 pub fn get_route_template(&self, id: &str) -> Option<RouteTemplateSpec> {
802 self.template_registry.get(id)
803 }
804
805 pub fn template_ids(&self) -> Vec<String> {
807 self.template_registry.template_ids()
808 }
809
810 pub fn record_template_instance(&self, record: TemplateInstanceRecord) {
812 self.template_registry.record_instance(record)
813 }
814
815 pub fn template_instances(&self, template_id: &str) -> Vec<TemplateInstanceRecord> {
817 self.template_registry.instances(template_id)
818 }
819
820 pub fn register_idempotent_repository(
827 &mut self,
828 name: impl Into<String>,
829 repo: Arc<dyn camel_api::IdempotentRepository>,
830 ) -> Result<(), RegistryError> {
831 self.idempotent_repositories.register(name, repo)
832 }
833
834 pub fn idempotent_repository(
836 &self,
837 name: &str,
838 ) -> Option<Arc<dyn camel_api::IdempotentRepository>> {
839 self.idempotent_repositories.get(name)
840 }
841
842 pub fn register_claim_check_repository(
849 &mut self,
850 name: impl Into<String>,
851 repo: Arc<dyn camel_api::ClaimCheckRepository>,
852 ) -> Result<(), RegistryError> {
853 self.claim_check_repositories.register(name, repo)
854 }
855
856 pub fn claim_check_repository(
858 &self,
859 name: &str,
860 ) -> Option<Arc<dyn camel_api::ClaimCheckRepository>> {
861 self.claim_check_repositories.get(name)
862 }
863}
864
865impl ComponentRegistrar for CamelContext {
866 fn register_component_dyn(&mut self, component: Arc<dyn Component>) {
867 let scheme = component.scheme().to_string();
868 self.registry
869 .lock()
870 .expect("mutex poisoned: another thread panicked while holding this lock") .register(component);
872 trace!(scheme, "Registered component");
873 }
874}
875
876impl ComponentContext for CamelContext {
877 fn resolve_component(&self, scheme: &str) -> Option<Arc<dyn Component>> {
878 self.registry.lock().ok()?.get(scheme)
879 }
880
881 fn resolve_language(&self, name: &str) -> Option<Arc<dyn Language>> {
882 self.languages.lock().ok()?.get(name).cloned()
883 }
884
885 fn metrics(&self) -> Arc<dyn MetricsCollector> {
886 Arc::clone(&self.metrics)
887 }
888
889 fn health(&self) -> Arc<dyn camel_component_api::HealthCheckRegistry> {
890 Arc::clone(&self.health_registry) as Arc<dyn camel_component_api::HealthCheckRegistry>
893 }
894
895 fn platform_service(&self) -> Arc<dyn PlatformService> {
896 Arc::clone(&self.platform_service)
897 }
898
899 fn register_route_health_check(
900 &self,
901 route_id: &str,
902 check: Arc<dyn camel_api::AsyncHealthCheck>,
903 ) {
904 self.health_registry.register_for_route(route_id, check);
905 }
906
907 fn unregister_route_health_check(&self, route_id: &str) {
908 self.health_registry.unregister_for_route(route_id);
909 }
910}
911
912#[async_trait::async_trait]
913impl camel_api::HealthSource for CamelContext {
914 async fn liveness(&self) -> camel_api::HealthStatus {
915 let has_failed = self
916 .services
917 .iter()
918 .any(|s| s.status() == camel_api::ServiceStatus::Failed);
919 if has_failed {
920 camel_api::HealthStatus::Unhealthy
921 } else {
922 camel_api::HealthStatus::Healthy
923 }
924 }
925
926 async fn readiness(&self) -> camel_api::HealthStatus {
927 let has_failed = self
928 .services
929 .iter()
930 .any(|s| s.status() == camel_api::ServiceStatus::Failed);
931 if has_failed {
932 return camel_api::HealthStatus::Unhealthy;
933 }
934 let has_stopped = self
935 .services
936 .iter()
937 .any(|s| s.status() == camel_api::ServiceStatus::Stopped);
938 if has_stopped {
939 return camel_api::HealthStatus::Degraded;
940 }
941 self.health_registry.check_all().await.status
942 }
943
944 async fn health_report(&self) -> camel_api::HealthReport {
945 let mut report = self.health_registry.check_all().await;
946 let mut worst = report.status;
947 for service in &self.services {
948 let svc_status = service.status();
949 let health = match svc_status {
950 camel_api::ServiceStatus::Started => camel_api::HealthStatus::Healthy,
951 camel_api::ServiceStatus::Stopped => camel_api::HealthStatus::Degraded,
952 camel_api::ServiceStatus::Failed => camel_api::HealthStatus::Unhealthy,
953 };
954 if matches!(worst, camel_api::HealthStatus::Healthy)
955 && matches!(
956 health,
957 camel_api::HealthStatus::Degraded | camel_api::HealthStatus::Unhealthy
958 )
959 {
960 worst = health;
961 }
962 if matches!(worst, camel_api::HealthStatus::Degraded)
963 && matches!(health, camel_api::HealthStatus::Unhealthy)
964 {
965 worst = health;
966 }
967 report.services.push(camel_api::ServiceHealth {
968 name: service.name().to_string(),
969 status: svc_status,
970 message: None,
971 });
972 }
973 report.status = worst;
974 report
975 }
976
977 async fn startup(&self) -> camel_api::HealthStatus {
978 camel_api::HealthStatus::Healthy
979 }
980}
981
982#[cfg(test)]
983#[path = "context_tests.rs"]
984mod context_tests;