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, InFlightGauge, Lifecycle, MetricsCollector,
13 MetricsHandle, PlatformIdentity, PlatformService, ReadinessGate, RouteTemplateSpec,
14 RuntimeCommandBus, 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 shutdown_token_slot: Arc<std::sync::Mutex<CancellationToken>>,
57 metrics: Arc<MetricsHandle>,
62 metrics_levers: MetricsLeversConfig,
68 platform_service: Arc<dyn PlatformService>,
70 languages: SharedLanguageRegistry,
71 shutdown_timeout: std::time::Duration,
72 drain_timeout: std::time::Duration,
76 services: Vec<Box<dyn Lifecycle>>,
77 health_registry: Arc<HealthCheckRegistry>,
78 component_configs: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
79 function_invoker: Option<Arc<dyn FunctionInvoker>>,
80 template_registry: Arc<TemplateRegistry>,
81 idempotent_repositories: crate::registry::SharedIdempotentRegistry,
82 claim_check_repositories: crate::registry::SharedClaimCheckRegistry,
83 cache_repositories: crate::registry::SharedCacheRegistry,
84 startup_checks: Vec<Box<dyn ConfigCheck>>,
88 build_version: &'static str,
92 build_git_sha: &'static str,
93 build_started_at: std::time::Instant,
95 in_flight_total: Arc<InFlightGauge>,
101}
102
103pub(crate) struct FromParts {
106 pub(crate) registry: Arc<std::sync::Mutex<Registry>>,
107 pub(crate) route_controller: RouteControllerHandle,
108 pub(crate) _actor_join: tokio::task::JoinHandle<()>,
109 pub(crate) supervision_join: Option<tokio::task::JoinHandle<()>>,
110 pub(crate) runtime: Arc<RuntimeBus>,
111 pub(crate) cancel_token: CancellationToken,
112 pub(crate) shutdown_token_slot: Arc<std::sync::Mutex<CancellationToken>>,
113 pub(crate) metrics: Arc<MetricsHandle>,
114 pub(crate) platform_service: Arc<dyn PlatformService>,
115 pub(crate) languages: SharedLanguageRegistry,
116 pub(crate) shutdown_timeout: std::time::Duration,
117 pub(crate) drain_timeout: std::time::Duration,
118 pub(crate) services: Vec<Box<dyn Lifecycle>>,
119 pub(crate) health_registry: Arc<HealthCheckRegistry>,
120 pub(crate) component_configs: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
121 pub(crate) function_invoker: Option<Arc<dyn FunctionInvoker>>,
122 pub(crate) template_registry: Arc<TemplateRegistry>,
123 pub(crate) idempotent_repositories: crate::registry::SharedIdempotentRegistry,
124 pub(crate) claim_check_repositories: crate::registry::SharedClaimCheckRegistry,
125 pub(crate) cache_repositories: crate::registry::SharedCacheRegistry,
126 pub(crate) startup_checks: Vec<Box<dyn ConfigCheck>>,
127 pub(crate) build_version: &'static str,
128 pub(crate) build_git_sha: &'static str,
129 pub(crate) build_started_at: std::time::Instant,
130 pub(crate) in_flight_total: Arc<InFlightGauge>,
131}
132
133impl CamelContext {
134 pub(crate) fn from_parts(parts: FromParts) -> Self {
135 Self {
136 registry: parts.registry,
137 route_controller: parts.route_controller,
138 actor_join: Some(parts._actor_join),
139 supervision_join: parts.supervision_join,
140 runtime: parts.runtime,
141 cancel_token: parts.cancel_token,
142 shutdown_token_slot: parts.shutdown_token_slot,
143 metrics: parts.metrics,
144 metrics_levers: MetricsLeversConfig::default(),
145 platform_service: parts.platform_service,
146 languages: parts.languages,
147 shutdown_timeout: parts.shutdown_timeout,
148 drain_timeout: parts.drain_timeout,
149 services: parts.services,
150 health_registry: parts.health_registry,
151 component_configs: parts.component_configs,
152 function_invoker: parts.function_invoker,
153 template_registry: parts.template_registry,
154 idempotent_repositories: parts.idempotent_repositories,
155 claim_check_repositories: parts.claim_check_repositories,
156 cache_repositories: parts.cache_repositories,
157 startup_checks: parts.startup_checks,
158 build_version: parts.build_version,
159 build_git_sha: parts.build_git_sha,
160 build_started_at: parts.build_started_at,
161 in_flight_total: parts.in_flight_total,
162 }
163 }
164}
165
166#[derive(Clone)]
170pub struct RuntimeExecutionHandle {
171 pub(crate) controller: RouteControllerHandle,
172 pub(crate) runtime: Arc<RuntimeBus>,
173 pub(crate) function_invoker: Option<Arc<dyn FunctionInvoker>>,
174 #[cfg(test)]
178 #[allow(clippy::type_complexity)]
179 pub(crate) test_lifecycle_inject: Arc<std::sync::Mutex<Option<Vec<Arc<dyn StepLifecycle>>>>>,
180}
181
182impl RuntimeExecutionHandle {
183 pub(crate) async fn add_route_definition(
184 &self,
185 definition: RouteDefinition,
186 ) -> Result<(), CamelError> {
187 use crate::lifecycle::application::ports::RouteRegistrationPort;
188 self.runtime
189 .register_route(definition)
190 .await
191 .map_err(Into::into)
192 }
193
194 #[allow(dead_code)]
197 pub(crate) async fn compile_route_definition(
198 &self,
199 definition: RouteDefinition,
200 ) -> Result<camel_api::BoxProcessor, CamelError> {
201 self.controller.compile_route_definition(definition).await
202 }
203
204 #[allow(dead_code)] pub(crate) async fn compile_route_definition_with_generation(
206 &self,
207 definition: RouteDefinition,
208 generation: u64,
209 ) -> Result<camel_api::BoxProcessor, CamelError> {
210 self.controller
211 .compile_route_definition_with_generation(definition, generation)
212 .await
213 }
214
215 pub(crate) async fn compile_route_definition_pipeline(
216 &self,
217 definition: RouteDefinition,
218 generation: u64,
219 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
220 self.controller
221 .compile_route_definition_pipeline(definition, generation)
222 .await
223 }
224
225 pub(crate) async fn compile_route_definition_dry_pipeline(
228 &self,
229 definition: RouteDefinition,
230 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
231 self.controller
232 .compile_route_definition_dry_pipeline(definition)
233 .await
234 }
235
236 pub(crate) async fn prepare_route_definition_with_generation(
237 &self,
238 definition: RouteDefinition,
239 generation: u64,
240 ) -> Result<crate::lifecycle::domain::route_compilation::PreparedRoute, CamelError> {
241 self.controller
242 .prepare_route_definition_with_generation(definition, generation)
243 .await
244 }
245
246 pub(crate) async fn insert_prepared_route(
247 &self,
248 prepared: crate::lifecycle::domain::route_compilation::PreparedRoute,
249 ) -> Result<(), CamelError> {
250 self.controller.insert_prepared_route(prepared).await
251 }
252
253 pub(crate) async fn discard_prepared_staging(&self, route_id: &str) -> Result<(), CamelError> {
254 self.controller.discard_prepared_staging(route_id).await
255 }
256
257 pub(crate) async fn remove_route_preserving_functions(
258 &self,
259 route_id: String,
260 ) -> Result<(), CamelError> {
261 self.controller
262 .remove_route_preserving_functions(route_id)
263 .await
264 }
265
266 pub(crate) async fn register_route_aggregate(
267 &self,
268 route_id: String,
269 ) -> Result<(), CamelError> {
270 self.runtime.register_aggregate_only(route_id).await
271 }
272
273 pub(crate) async fn swap_route_pipeline(
274 &self,
275 route_id: &str,
276 pipeline: camel_api::BoxProcessor,
277 ) -> Result<(), CamelError> {
278 self.controller.swap_pipeline(route_id, pipeline).await
279 }
280
281 pub(crate) async fn stop_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
283 self.controller.stop_route_reload(route_id).await
284 }
285
286 pub(crate) async fn start_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
288 self.controller.start_route_reload(route_id).await
289 }
290
291 pub(crate) async fn swap_route_pipeline_raw(
294 &self,
295 route_id: &str,
296 pipeline: camel_api::BoxProcessor,
297 lifecycle: Vec<Arc<dyn camel_api::StepLifecycle>>,
298 ) -> Result<(), CamelError> {
299 self.controller
300 .swap_pipeline_raw(route_id, pipeline, lifecycle)
301 .await
302 }
303
304 pub(crate) async fn execute_runtime_command(
305 &self,
306 cmd: camel_api::RuntimeCommand,
307 ) -> Result<camel_api::RuntimeCommandResult, CamelError> {
308 self.runtime.execute(cmd).await
309 }
310
311 pub(crate) async fn runtime_route_status(
312 &self,
313 route_id: &str,
314 ) -> Result<Option<String>, CamelError> {
315 match self
316 .runtime
317 .ask(camel_api::RuntimeQuery::GetRouteStatus {
318 route_id: route_id.to_string(),
319 })
320 .await
321 {
322 Ok(camel_api::RuntimeQueryResult::RouteStatus { status, .. }) => Ok(Some(status)),
323 Ok(_) => Err(CamelError::RouteError(
324 "unexpected runtime query response for route status".to_string(),
325 )),
326 Err(CamelError::RouteError(msg)) if msg.contains("not found") => Ok(None),
327 Err(err) => Err(err),
328 }
329 }
330
331 pub(crate) async fn runtime_route_ids(&self) -> Result<Vec<String>, CamelError> {
332 match self.runtime.ask(camel_api::RuntimeQuery::ListRoutes).await {
333 Ok(camel_api::RuntimeQueryResult::Routes { route_ids }) => Ok(route_ids),
334 Ok(_) => Err(CamelError::RouteError(
335 "unexpected runtime query response for route listing".to_string(),
336 )),
337 Err(err) => Err(err),
338 }
339 }
340
341 pub(crate) async fn route_source_hash(&self, route_id: &str) -> Option<u64> {
342 self.controller.route_source_hash(route_id).await
343 }
344
345 pub(crate) async fn in_flight_count(&self, route_id: &str) -> Result<u64, CamelError> {
346 if !self.controller.route_exists(route_id).await? {
347 return Err(CamelError::RouteError(format!(
348 "Route '{}' not found",
349 route_id
350 )));
351 }
352 Ok(self
353 .controller
354 .in_flight_count(route_id)
355 .await?
356 .unwrap_or(0))
357 }
358
359 pub(crate) async fn route_has_lifecycle(&self, route_id: &str) -> bool {
361 self.controller
362 .route_has_lifecycle(route_id)
363 .await
364 .unwrap_or(false)
365 }
366
367 pub(crate) fn function_invoker(&self) -> Option<Arc<dyn FunctionInvoker>> {
368 self.function_invoker.clone()
369 }
370
371 #[cfg(test)]
372 pub(crate) async fn force_start_route_for_test(
373 &self,
374 route_id: &str,
375 ) -> Result<(), CamelError> {
376 self.controller.start_route(route_id).await
377 }
378
379 pub async fn controller_route_count_for_test(&self) -> usize {
380 self.controller.route_count().await.unwrap_or(0)
381 }
382}
383
384#[async_trait::async_trait]
385impl crate::hot_reload::ports::ReloadExecutorPort for RuntimeExecutionHandle {
386 async fn add_route_definition(&self, definition: RouteDefinition) -> Result<(), CamelError> {
387 RuntimeExecutionHandle::add_route_definition(self, definition).await
388 }
389
390 async fn compile_route_definition_pipeline(
391 &self,
392 definition: RouteDefinition,
393 generation: u64,
394 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
395 RuntimeExecutionHandle::compile_route_definition_pipeline(self, definition, generation)
396 .await
397 }
398
399 async fn compile_route_definition_dry_pipeline(
400 &self,
401 definition: RouteDefinition,
402 ) -> Result<crate::lifecycle::domain::CompiledPipeline, CamelError> {
403 RuntimeExecutionHandle::compile_route_definition_dry_pipeline(self, definition).await
404 }
405
406 async fn prepare_route_definition_with_generation(
407 &self,
408 definition: RouteDefinition,
409 generation: u64,
410 ) -> Result<crate::lifecycle::domain::route_compilation::PreparedRoute, CamelError> {
411 RuntimeExecutionHandle::prepare_route_definition_with_generation(
412 self, definition, generation,
413 )
414 .await
415 }
416
417 async fn insert_prepared_route(
418 &self,
419 prepared: crate::lifecycle::domain::route_compilation::PreparedRoute,
420 ) -> Result<(), CamelError> {
421 RuntimeExecutionHandle::insert_prepared_route(self, prepared).await
422 }
423
424 async fn discard_prepared_staging(&self, route_id: &str) -> Result<(), CamelError> {
425 RuntimeExecutionHandle::discard_prepared_staging(self, route_id).await
426 }
427
428 async fn remove_route_preserving_functions(&self, route_id: String) -> Result<(), CamelError> {
429 RuntimeExecutionHandle::remove_route_preserving_functions(self, route_id).await
430 }
431
432 async fn register_route_aggregate(&self, route_id: String) -> Result<(), CamelError> {
433 RuntimeExecutionHandle::register_route_aggregate(self, route_id).await
434 }
435
436 async fn swap_route_pipeline(
437 &self,
438 route_id: &str,
439 pipeline: camel_api::BoxProcessor,
440 ) -> Result<(), CamelError> {
441 RuntimeExecutionHandle::swap_route_pipeline(self, route_id, pipeline).await
442 }
443
444 async fn stop_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
445 RuntimeExecutionHandle::stop_route_reload(self, route_id).await
446 }
447
448 async fn start_route_reload(&self, route_id: &str) -> Result<(), CamelError> {
449 RuntimeExecutionHandle::start_route_reload(self, route_id).await
450 }
451
452 async fn swap_route_pipeline_raw(
453 &self,
454 route_id: &str,
455 pipeline: camel_api::BoxProcessor,
456 lifecycle: Vec<std::sync::Arc<dyn camel_api::StepLifecycle>>,
457 ) -> Result<(), CamelError> {
458 RuntimeExecutionHandle::swap_route_pipeline_raw(self, route_id, pipeline, lifecycle).await
459 }
460
461 async fn execute_runtime_command(
462 &self,
463 cmd: camel_api::RuntimeCommand,
464 ) -> Result<camel_api::RuntimeCommandResult, CamelError> {
465 RuntimeExecutionHandle::execute_runtime_command(self, cmd).await
466 }
467
468 async fn runtime_route_status(&self, route_id: &str) -> Result<Option<String>, CamelError> {
469 RuntimeExecutionHandle::runtime_route_status(self, route_id).await
470 }
471
472 async fn in_flight_count(&self, route_id: &str) -> Result<u64, CamelError> {
473 RuntimeExecutionHandle::in_flight_count(self, route_id).await
474 }
475
476 async fn route_has_lifecycle(&self, route_id: &str) -> bool {
477 RuntimeExecutionHandle::route_has_lifecycle(self, route_id).await
478 }
479
480 #[cfg(test)]
481 fn take_test_lifecycle_inject(
482 &self,
483 ) -> Option<Vec<std::sync::Arc<dyn camel_api::StepLifecycle>>> {
484 self.test_lifecycle_inject.lock().unwrap().take()
485 }
486}
487
488impl CamelContext {
489 pub fn builder() -> CamelContextBuilder {
490 CamelContextBuilder::new()
491 }
492
493 pub async fn set_error_handler(&mut self, config: ErrorHandlerConfig) {
495 let _ = self.route_controller.set_error_handler(config).await;
496 }
497
498 pub async fn set_bind_exposure_acks(
502 &mut self,
503 acks: crate::lifecycle::adapters::route_controller_trait::BindExposureAcks,
504 ) {
505 let _ = self.route_controller.set_bind_exposure_acks(acks).await;
506 }
507
508 pub async fn set_tracing(&mut self, enabled: bool) {
510 let config = TracerConfig {
511 enabled,
512 ..Default::default()
513 };
514 self.metrics_levers = config.metrics_levers.clone();
517 let _ = self.route_controller.set_tracer_config(config).await;
518 }
519
520 pub async fn set_tracer_config(&mut self, config: TracerConfig) {
522 self.metrics_levers = config.metrics_levers.clone();
525 let _ = self.route_controller.set_tracer_config(config).await;
526 }
527
528 pub async fn with_tracing(mut self) -> Self {
530 self.set_tracing(true).await;
531 self
532 }
533
534 pub async fn with_tracer_config(mut self, config: TracerConfig) -> Self {
538 self.set_tracer_config(config).await;
539 self
540 }
541
542 pub fn with_lifecycle<L: Lifecycle + 'static>(mut self, service: L) -> Self {
551 self.add_lifecycle(service);
552 self
553 }
554
555 pub fn add_lifecycle<L: Lifecycle + 'static>(&mut self, service: L) {
560 if let Some(collector) = service.as_metrics_collector() {
561 self.metrics.register(collector);
565 self.metrics
570 .record_build_info(self.build_version, self.build_git_sha);
571 self.metrics
572 .record_uptime(self.build_started_at.elapsed().as_secs_f64());
573 }
574 if let Some(invoker) = service.as_function_invoker() {
575 self.function_invoker = Some(invoker.clone());
576 if let Err(e) = self.route_controller.try_set_function_invoker(invoker) {
577 tracing::debug!("Failed to propagate function invoker to route controller: {e}");
578 }
579 }
580
581 self.services.push(Box::new(service));
582 }
583
584 pub fn register_component<C: Component + 'static>(&mut self, component: C) {
590 self.register_component_dyn(Arc::new(component));
591 }
592
593 pub async fn set_intercept_rules(&self, rules: InterceptRules) -> Result<(), CamelError> {
600 self.route_controller.set_intercept_rules(rules).await
601 }
602
603 pub fn add_startup_check(&mut self, check: Box<dyn ConfigCheck>) {
612 self.startup_checks.push(check);
613 }
614
615 pub fn register_language(
622 &mut self,
623 name: impl Into<String>,
624 lang: Box<dyn Language>,
625 ) -> Result<(), LanguageRegistryError> {
626 let name = name.into();
627 let mut languages = self
628 .languages
629 .lock()
630 .expect("mutex poisoned: another thread panicked while holding this lock"); if languages.contains_key(&name) {
632 return Err(LanguageRegistryError::AlreadyRegistered { name });
633 }
634 languages.insert(name, Arc::from(lang));
635 Ok(())
636 }
637
638 pub fn resolve_language(&self, name: &str) -> Option<Arc<dyn Language>> {
640 let languages = self
641 .languages
642 .lock()
643 .expect("mutex poisoned: another thread panicked while holding this lock"); languages.get(name).cloned()
645 }
646
647 pub async fn add_route_definition(
651 &self,
652 definition: RouteDefinition,
653 ) -> Result<(), CamelError> {
654 use crate::lifecycle::application::ports::RouteRegistrationPort;
655 debug!(
656 from = definition.from_uri(),
657 route_id = %definition.route_id(),
658 "Adding route definition"
659 );
660 self.runtime
661 .register_route(definition)
662 .await
663 .map_err(Into::into)
664 }
665
666 pub fn registry(&self) -> std::sync::MutexGuard<'_, Registry> {
668 self.registry
669 .lock()
670 .expect("mutex poisoned: another thread panicked while holding this lock") }
672
673 pub fn registry_arc(&self) -> Arc<std::sync::Mutex<Registry>> {
675 Arc::clone(&self.registry)
676 }
677
678 pub fn runtime_execution_handle(&self) -> RuntimeExecutionHandle {
680 RuntimeExecutionHandle {
681 controller: self.route_controller.clone(),
682 runtime: Arc::clone(&self.runtime),
683 function_invoker: self.function_invoker.clone(),
684 #[cfg(test)]
685 test_lifecycle_inject: Arc::new(std::sync::Mutex::new(None)),
686 }
687 }
688
689 pub fn metrics(&self) -> Arc<dyn MetricsCollector> {
691 Arc::clone(&self.metrics) as Arc<dyn MetricsCollector>
692 }
693
694 pub fn total_in_flight(&self) -> u64 {
704 self.in_flight_total.total()
705 }
706
707 pub fn in_flight_gauge(&self) -> Arc<InFlightGauge> {
711 Arc::clone(&self.in_flight_total)
712 }
713
714 pub fn platform_service(&self) -> Arc<dyn PlatformService> {
716 Arc::clone(&self.platform_service)
717 }
718
719 pub fn readiness_gate(&self) -> Arc<dyn ReadinessGate> {
721 self.platform_service.readiness_gate()
722 }
723
724 pub fn platform_identity(&self) -> PlatformIdentity {
726 self.platform_service.identity()
727 }
728
729 pub fn leadership(&self) -> Arc<dyn camel_api::LeadershipService> {
731 self.platform_service.leadership()
732 }
733
734 pub fn runtime(&self) -> Arc<dyn camel_api::RuntimeHandle> {
736 self.runtime.clone()
737 }
738
739 pub fn producer_context(&self) -> camel_api::ProducerContext {
741 camel_api::ProducerContext::new().with_runtime(self.runtime())
742 }
743
744 pub async fn runtime_route_status(&self, route_id: &str) -> Result<Option<String>, CamelError> {
746 match self
747 .runtime()
748 .ask(camel_api::RuntimeQuery::GetRouteStatus {
749 route_id: route_id.to_string(),
750 })
751 .await
752 {
753 Ok(camel_api::RuntimeQueryResult::RouteStatus { status, .. }) => Ok(Some(status)),
754 Ok(_) => Err(CamelError::RouteError(
755 "unexpected runtime query response for route status".to_string(),
756 )),
757 Err(CamelError::RouteError(msg)) if msg.contains("not found") => Ok(None),
758 Err(err) => Err(err),
759 }
760 }
761
762 pub async fn start(&mut self) -> Result<(), CamelError> {
770 crate::lifecycle::application::context_lifecycle::start_context(
771 &mut self.services,
772 &mut self.startup_checks,
773 &self.runtime,
774 &self.route_controller,
775 &mut self.cancel_token,
776 &self.shutdown_token_slot,
777 )
778 .await?;
779 self.route_controller.mark_started().await
783 }
784
785 pub async fn stop(&mut self) -> Result<(), CamelError> {
787 self.stop_timeout(self.shutdown_timeout).await
788 }
789
790 pub async fn stop_timeout(&mut self, _timeout: std::time::Duration) -> Result<(), CamelError> {
800 crate::lifecycle::application::context_lifecycle::stop_context(
801 &self.cancel_token,
802 &mut self.supervision_join,
803 &self.runtime,
804 &self.route_controller,
805 &mut self.services,
806 self.drain_timeout,
807 )
808 .await
809 }
810
811 pub fn shutdown_timeout(&self) -> std::time::Duration {
813 self.shutdown_timeout
814 }
815
816 pub fn set_shutdown_timeout(&mut self, timeout: std::time::Duration) {
818 self.shutdown_timeout = timeout;
819 }
820
821 pub fn drain_timeout(&self) -> std::time::Duration {
825 self.drain_timeout
826 }
827
828 pub fn set_drain_timeout(&mut self, timeout: std::time::Duration) {
831 self.drain_timeout = timeout;
832 }
833
834 #[cfg(test)]
837 pub(crate) fn take_actor_join(&mut self) -> Option<tokio::task::JoinHandle<()>> {
838 self.actor_join.take()
839 }
840
841 pub async fn abort(&mut self) {
849 crate::lifecycle::application::context_lifecycle::abort_context(
850 &self.cancel_token,
851 &mut self.supervision_join,
852 &self.runtime,
853 &self.route_controller as &dyn crate::lifecycle::application::ports::RouteOrderingPort,
854 &self.route_controller
855 as &dyn crate::lifecycle::application::ports::RouteDestructiveTeardownPort,
856 &mut self.services,
857 self.health_registry.cancel_token(),
858 &mut self.actor_join,
859 )
860 .await
861 }
862
863 pub async fn health_check(&self) -> HealthReport {
865 use camel_api::HealthSource;
866 self.health_report().await
867 }
868
869 pub fn health_registry(&self) -> Arc<HealthCheckRegistry> {
870 Arc::clone(&self.health_registry)
871 }
872
873 pub fn set_component_config<T: 'static + Send + Sync>(&mut self, config: T) {
875 self.component_configs
876 .insert(TypeId::of::<T>(), Box::new(config));
877 }
878
879 pub fn get_component_config<T: 'static + Send + Sync>(&self) -> Option<&T> {
881 self.component_configs
882 .get(&TypeId::of::<T>())
883 .and_then(|b| b.downcast_ref::<T>())
884 }
885
886 pub fn component_metadata(&self, scheme: &str) -> Option<ComponentMetadata> {
890 self.registry.lock().ok()?.get_metadata(scheme)
891 }
892
893 pub fn all_component_metadata(&self) -> Vec<ComponentMetadata> {
895 self.registry
896 .lock()
897 .expect("mutex poisoned: another thread panicked while holding this lock") .all_metadata()
899 }
900
901 pub fn metadata_catalog(
908 &self,
909 ) -> crate::component_metadata_catalog::RuntimeComponentMetadataCatalog {
910 crate::component_metadata_catalog::RuntimeComponentMetadataCatalog::new(Arc::clone(
911 &self.registry,
912 ))
913 }
914
915 pub fn add_route_template(&self, spec: RouteTemplateSpec) -> Result<(), CamelError> {
921 self.template_registry.register(spec)
922 }
923
924 pub fn get_route_template(&self, id: &str) -> Option<RouteTemplateSpec> {
926 self.template_registry.get(id)
927 }
928
929 pub fn template_ids(&self) -> Vec<String> {
931 self.template_registry.template_ids()
932 }
933
934 pub fn record_template_instance(&self, record: TemplateInstanceRecord) {
936 self.template_registry.record_instance(record)
937 }
938
939 pub fn template_instances(&self, template_id: &str) -> Vec<TemplateInstanceRecord> {
941 self.template_registry.instances(template_id)
942 }
943
944 pub fn register_idempotent_repository(
951 &mut self,
952 name: impl Into<String>,
953 repo: Arc<dyn camel_api::IdempotentRepository>,
954 ) -> Result<(), RegistryError> {
955 self.idempotent_repositories.register(name, repo)
956 }
957
958 pub fn idempotent_repository(
960 &self,
961 name: &str,
962 ) -> Option<Arc<dyn camel_api::IdempotentRepository>> {
963 self.idempotent_repositories.get(name)
964 }
965
966 pub fn register_claim_check_repository(
973 &mut self,
974 name: impl Into<String>,
975 repo: Arc<dyn camel_api::ClaimCheckRepository>,
976 ) -> Result<(), RegistryError> {
977 self.claim_check_repositories.register(name, repo)
978 }
979
980 pub fn claim_check_repository(
982 &self,
983 name: &str,
984 ) -> Option<Arc<dyn camel_api::ClaimCheckRepository>> {
985 self.claim_check_repositories.get(name)
986 }
987
988 pub fn register_cache_repository(
995 &mut self,
996 name: impl Into<String>,
997 repo: Arc<dyn camel_api::CacheRepository>,
998 ) -> Result<(), RegistryError> {
999 self.cache_repositories.register(name, repo)
1000 }
1001
1002 pub fn replace_cache_repository(
1006 &mut self,
1007 name: impl Into<String>,
1008 repo: Arc<dyn camel_api::CacheRepository>,
1009 ) -> Option<Arc<dyn camel_api::CacheRepository>> {
1010 self.cache_repositories.register_or_replace(name, repo)
1011 }
1012
1013 pub fn cache_repository(&self, name: &str) -> Option<Arc<dyn camel_api::CacheRepository>> {
1015 self.cache_repositories.get(name)
1016 }
1017
1018 pub fn shutdown_token(&self) -> CancellationToken {
1023 self.cancel_token.clone()
1024 }
1025
1026 pub fn shutdown_token_slot(&self) -> Arc<std::sync::Mutex<CancellationToken>> {
1037 Arc::clone(&self.shutdown_token_slot)
1038 }
1039}
1040
1041impl Drop for CamelContext {
1054 fn drop(&mut self) {
1055 if let Some(handle) = self.actor_join.take() {
1056 handle.abort();
1057 }
1058 if let Some(handle) = self.supervision_join.take() {
1059 handle.abort();
1060 }
1061 }
1062}
1063
1064impl ComponentRegistrar for CamelContext {
1065 fn register_component_dyn(&mut self, component: Arc<dyn Component>) {
1066 let scheme = component.scheme().to_string();
1067 self.registry
1068 .lock()
1069 .expect("mutex poisoned: another thread panicked while holding this lock") .register(component);
1071 trace!(scheme, "Registered component");
1072 }
1073}
1074
1075impl ComponentContext for CamelContext {
1076 fn resolve_component(&self, scheme: &str) -> Option<Arc<dyn Component>> {
1077 self.registry.lock().ok()?.get(scheme)
1078 }
1079
1080 fn resolve_language(&self, name: &str) -> Option<Arc<dyn Language>> {
1081 self.languages.lock().ok()?.get(name).cloned()
1082 }
1083
1084 fn metrics(&self) -> Arc<dyn MetricsCollector> {
1085 Arc::clone(&self.metrics) as Arc<dyn MetricsCollector>
1086 }
1087
1088 fn component_metrics_enabled(&self) -> bool {
1094 self.metrics_levers.components_enabled()
1095 }
1096
1097 fn health(&self) -> Arc<dyn camel_component_api::HealthCheckRegistry> {
1098 Arc::clone(&self.health_registry) as Arc<dyn camel_component_api::HealthCheckRegistry>
1101 }
1102
1103 fn platform_service(&self) -> Arc<dyn PlatformService> {
1104 Arc::clone(&self.platform_service)
1105 }
1106
1107 fn register_route_health_check(
1108 &self,
1109 route_id: &str,
1110 check: Arc<dyn camel_api::AsyncHealthCheck>,
1111 ) {
1112 self.health_registry.register_for_route(route_id, check);
1113 }
1114
1115 fn unregister_route_health_check(&self, route_id: &str) {
1116 self.health_registry.unregister_for_route(route_id);
1117 }
1118
1119 fn in_flight_counter(&self) -> Option<Arc<InFlightGauge>> {
1123 Some(Arc::clone(&self.in_flight_total))
1124 }
1125
1126 fn shutdown_token(&self) -> Option<CancellationToken> {
1130 Some(CamelContext::shutdown_token(self))
1131 }
1132}
1133
1134#[async_trait::async_trait]
1135impl camel_api::HealthSource for CamelContext {
1136 async fn liveness(&self) -> camel_api::HealthStatus {
1137 let has_failed = self
1138 .services
1139 .iter()
1140 .any(|s| s.status() == camel_api::ServiceStatus::Failed);
1141 if has_failed {
1142 camel_api::HealthStatus::Unhealthy
1143 } else {
1144 camel_api::HealthStatus::Healthy
1145 }
1146 }
1147
1148 async fn readiness(&self) -> camel_api::HealthStatus {
1149 let has_failed = self
1150 .services
1151 .iter()
1152 .any(|s| s.status() == camel_api::ServiceStatus::Failed);
1153 if has_failed {
1154 return camel_api::HealthStatus::Unhealthy;
1155 }
1156 let has_stopped = self
1157 .services
1158 .iter()
1159 .any(|s| s.status() == camel_api::ServiceStatus::Stopped);
1160 if has_stopped {
1161 return camel_api::HealthStatus::Degraded;
1162 }
1163 self.health_registry.check_all().await.status
1164 }
1165
1166 async fn health_report(&self) -> camel_api::HealthReport {
1167 let mut report = self.health_registry.check_all().await;
1168 let mut worst = report.status;
1169 for service in &self.services {
1170 let svc_status = service.status();
1171 let health = match svc_status {
1172 camel_api::ServiceStatus::Started => camel_api::HealthStatus::Healthy,
1173 camel_api::ServiceStatus::Stopped => camel_api::HealthStatus::Degraded,
1174 camel_api::ServiceStatus::Failed => camel_api::HealthStatus::Unhealthy,
1175 _ => camel_api::HealthStatus::Unhealthy,
1178 };
1179 if matches!(worst, camel_api::HealthStatus::Healthy)
1180 && matches!(
1181 health,
1182 camel_api::HealthStatus::Degraded | camel_api::HealthStatus::Unhealthy
1183 )
1184 {
1185 worst = health;
1186 }
1187 if matches!(worst, camel_api::HealthStatus::Degraded)
1188 && matches!(health, camel_api::HealthStatus::Unhealthy)
1189 {
1190 worst = health;
1191 }
1192 report.services.push(camel_api::ServiceHealth {
1193 name: service.name().to_string(),
1194 status: svc_status,
1195 message: None,
1196 });
1197 }
1198 report.status = worst;
1199 report
1200 }
1201
1202 async fn startup(&self) -> camel_api::HealthStatus {
1203 camel_api::HealthStatus::Healthy
1204 }
1205}
1206
1207#[cfg(test)]
1208#[path = "context_tests.rs"]
1209mod context_tests;