reactive_graph_runtime_impl/
runtime_impl.rs1use std::sync::Arc;
2use std::sync::atomic::AtomicBool;
3use std::sync::atomic::Ordering;
4use std::time::Duration;
5
6use async_trait::async_trait;
7use log::debug;
8use log::info;
9use springtime_di::Component;
10use springtime_di::component_alias;
11use tokio::time::error::Elapsed;
12
13use reactive_graph_behaviour_service_api::BehaviourSystem;
14use reactive_graph_behaviour_service_api::EntityBehaviourManager;
15use reactive_graph_behaviour_service_api::EntityBehaviourRegistry;
16use reactive_graph_behaviour_service_api::EntityComponentBehaviourManager;
17use reactive_graph_behaviour_service_api::EntityComponentBehaviourRegistry;
18use reactive_graph_behaviour_service_api::RelationBehaviourManager;
19use reactive_graph_behaviour_service_api::RelationBehaviourRegistry;
20use reactive_graph_behaviour_service_api::RelationComponentBehaviourManager;
21use reactive_graph_behaviour_service_api::RelationComponentBehaviourRegistry;
22use reactive_graph_command_api::CommandManager;
23use reactive_graph_command_api::CommandSystem;
24use reactive_graph_command_api::CommandTypeProvider;
25use reactive_graph_command_impl::CommandSystemImpl;
26use reactive_graph_config_api::ConfigManager;
27use reactive_graph_config_api::ConfigSystem;
28use reactive_graph_config_impl::ConfigSystemImpl;
29use reactive_graph_dynamic_graph_api::DynamicGraphQueryService;
30use reactive_graph_dynamic_graph_api::DynamicGraphSchemaManager;
31use reactive_graph_dynamic_graph_api::DynamicGraphSystem;
32use reactive_graph_graphql_api::GraphQLQueryService;
33use reactive_graph_graphql_api::GraphQLSchemaManager;
34use reactive_graph_graphql_api::GraphQLSystem;
35use reactive_graph_instance_system_api::EntityInstanceImportExportManager;
36use reactive_graph_instance_system_api::InstanceSystem;
37use reactive_graph_instance_system_api::RelationInstanceImportExportManager;
38use reactive_graph_lifecycle::Lifecycle;
39use reactive_graph_plugin_graphql_api::PluginGraphQLSystem;
40use reactive_graph_plugin_graphql_api::PluginQueryService;
41use reactive_graph_plugin_graphql_api::PluginSchemaManager;
42use reactive_graph_plugin_service_api::PluginContainerManager;
43use reactive_graph_plugin_service_api::PluginContextFactory;
44use reactive_graph_plugin_service_api::PluginRepositoryManager;
45use reactive_graph_plugin_service_api::PluginResolver;
46use reactive_graph_plugin_service_api::PluginSystem;
47use reactive_graph_reactive_service_api::ReactiveEntityManager;
48use reactive_graph_reactive_service_api::ReactiveFlowManager;
49use reactive_graph_reactive_service_api::ReactiveInstanceEventManager;
50use reactive_graph_reactive_service_api::ReactiveRelationManager;
51use reactive_graph_reactive_service_api::ReactiveSystem;
52use reactive_graph_remotes_api::InstanceService;
53use reactive_graph_remotes_api::RemotesManager;
54use reactive_graph_remotes_api::RemotesSystem;
55use reactive_graph_remotes_impl::RemotesSystemImpl;
56use reactive_graph_remotes_model::InstanceAddress;
57use reactive_graph_runtime_api::Runtime;
58use reactive_graph_runtime_graphql_api::RuntimeGraphQLSystem;
59use reactive_graph_runtime_graphql_api::RuntimeQueryService;
60use reactive_graph_runtime_graphql_api::RuntimeSchemaManager;
61use reactive_graph_runtime_service_api::RuntimeSystem;
62use reactive_graph_runtime_service_api::ShutdownManager;
63use reactive_graph_runtime_web_api::GraphQLServer;
64use reactive_graph_runtime_web_api::WebResourceManager;
65use reactive_graph_runtime_web_api::WebSystem;
66use reactive_graph_type_system_api::ComponentImportExportManager;
67use reactive_graph_type_system_api::ComponentManager;
68use reactive_graph_type_system_api::ComponentProviderRegistry;
69use reactive_graph_type_system_api::EntityTypeImportExportManager;
70use reactive_graph_type_system_api::EntityTypeManager;
71use reactive_graph_type_system_api::EntityTypeProviderRegistry;
72use reactive_graph_type_system_api::FlowTypeImportExportManager;
73use reactive_graph_type_system_api::FlowTypeManager;
74use reactive_graph_type_system_api::FlowTypeProviderRegistry;
75use reactive_graph_type_system_api::NamespaceManager;
76use reactive_graph_type_system_api::RelationTypeImportExportManager;
77use reactive_graph_type_system_api::RelationTypeManager;
78use reactive_graph_type_system_api::RelationTypeProviderRegistry;
79use reactive_graph_type_system_api::TypeSystem;
80use reactive_graph_type_system_api::TypeSystemEventManager;
81
82pub struct RunningState(Arc<AtomicBool>);
83
84fn create_running_state() -> RunningState {
85 RunningState(Arc::new(AtomicBool::new(false)))
86}
87
88#[derive(Component)]
89pub struct RuntimeImpl {
90 #[component(default = "create_running_state")]
91 running: RunningState,
92 type_system: Arc<dyn TypeSystem + Send + Sync>,
93 reactive_system: Arc<dyn ReactiveSystem + Send + Sync>,
94 behaviour_system: Arc<dyn BehaviourSystem + Send + Sync>,
95 instance_system: Arc<dyn InstanceSystem + Send + Sync>,
96 command_system: Arc<CommandSystemImpl>,
97 runtime_system: Arc<dyn RuntimeSystem + Send + Sync>,
98 remotes_system: Arc<RemotesSystemImpl>,
99 config_system: Arc<ConfigSystemImpl>,
100 graphql_system: Arc<dyn GraphQLSystem + Send + Sync>,
101 dynamic_graph_system: Arc<dyn DynamicGraphSystem + Send + Sync>,
102 runtime_graphql_system: Arc<dyn RuntimeGraphQLSystem + Send + Sync>,
103 plugin_graphql_system: Arc<dyn PluginGraphQLSystem + Send + Sync>,
104 plugin_system: Arc<dyn PluginSystem + Send + Sync>,
105 web_system: Arc<dyn WebSystem + Send + Sync>,
106}
107
108impl RuntimeImpl {
109 async fn wait_for_started_internal(&self) {
110 while !self.is_running() {
112 tokio::time::sleep(Duration::from_millis(100)).await;
113 }
114 }
115}
116
117#[async_trait]
118#[component_alias]
119impl Runtime for RuntimeImpl {
120 async fn config(&self) {
121 self.config_system.init().await;
122 }
123
124 async fn run(&self) {
125 let terminate = Arc::new(AtomicBool::new(false));
127 let (graphql_server_stop_sender, graphql_server_stop_receiver) = crossbeam::channel::unbounded::<()>();
129 let (graphql_server_stopped_sender, graphql_server_stopped_receiver) = crossbeam::channel::unbounded::<()>();
131 let graphql_server = self.web_system.get_graphql_server();
133 let graphql_server_handle = tokio::spawn(async move {
137 info!("Run the GraphQL server.");
139 graphql_server.serve(graphql_server_stop_receiver).await;
140 debug!("Successfully stopped GraphQL Server.");
141 let _result = graphql_server_stopped_sender.send(());
143 });
144
145 {
146 self.running.0.store(true, Ordering::Relaxed);
147 }
148
149 {
150 let _r_sigint = signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&terminate));
151 let _r_sigterm = signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&terminate));
152
153 let mut stopping = false;
154 while self.is_running() && !stopping && !terminate.load(Ordering::Relaxed) {
155 tokio::time::sleep(Duration::from_millis(100)).await;
156 let r = graphql_server_stopped_receiver.try_recv();
157 if r.is_ok() {
158 debug!("Stopping the main thread");
159 stopping = true;
160 }
161 if self.runtime_system.get_shutdown_manager().is_shutdown() {
162 stopping = true;
163 }
164 }
165 } debug!("Stopping the GraphQL server thread");
169 let _graphql_server_stop_result = graphql_server_stop_sender.send_timeout((), Duration::from_millis(100));
170
171 let _ = graphql_server_handle.await;
173 info!("Bye.");
174
175 {
178 self.running.0.store(false, Ordering::Relaxed);
179 }
180 }
181
182 fn stop(&self) {
183 {
184 self.running.0.store(false, Ordering::Relaxed);
185 }
186 }
187
188 fn is_running(&self) -> bool {
189 self.running.0.load(Ordering::Relaxed)
190 }
192
193 async fn wait_for_started(&self, timeout_duration: Duration) -> Result<(), Elapsed> {
194 tokio::time::timeout(timeout_duration, self.wait_for_started_internal()).await
195 }
196
197 async fn wait_for_stopped(&self) {
198 while self.is_running() {
199 tokio::time::sleep(Duration::from_millis(100)).await;
200 }
201 }
202
203 async fn wait_for_stopped_with_timeout(&self, timeout_duration: Duration) -> Result<(), Elapsed> {
204 tokio::time::timeout(timeout_duration, self.wait_for_stopped()).await
205 }
206
207 fn address(&self) -> InstanceAddress {
208 self.remotes_system.get_instance_service().get_instance_info().address()
209 }
210}
211
212impl TypeSystem for RuntimeImpl {
213 fn get_component_manager(&self) -> Arc<dyn ComponentManager + Send + Sync> {
214 self.type_system.get_component_manager()
215 }
216
217 fn get_component_import_export_manager(&self) -> Arc<dyn ComponentImportExportManager + Send + Sync> {
218 self.type_system.get_component_import_export_manager()
219 }
220
221 fn get_component_provider_registry(&self) -> Arc<dyn ComponentProviderRegistry + Send + Sync> {
222 self.type_system.get_component_provider_registry()
223 }
224
225 fn get_entity_type_manager(&self) -> Arc<dyn EntityTypeManager + Send + Sync> {
226 self.type_system.get_entity_type_manager()
227 }
228
229 fn get_entity_type_import_export_manager(&self) -> Arc<dyn EntityTypeImportExportManager + Send + Sync> {
230 self.type_system.get_entity_type_import_export_manager()
231 }
232
233 fn get_entity_type_provider_registry(&self) -> Arc<dyn EntityTypeProviderRegistry + Send + Sync> {
234 self.type_system.get_entity_type_provider_registry()
235 }
236
237 fn get_flow_type_manager(&self) -> Arc<dyn FlowTypeManager + Send + Sync> {
238 self.type_system.get_flow_type_manager()
239 }
240
241 fn get_flow_type_import_export_manager(&self) -> Arc<dyn FlowTypeImportExportManager + Send + Sync> {
242 self.type_system.get_flow_type_import_export_manager()
243 }
244
245 fn get_flow_type_provider_registry(&self) -> Arc<dyn FlowTypeProviderRegistry + Send + Sync> {
246 self.type_system.get_flow_type_provider_registry()
247 }
248
249 fn get_namespace_manager(&self) -> Arc<dyn NamespaceManager + Send + Sync> {
250 self.type_system.get_namespace_manager()
251 }
252
253 fn get_relation_type_manager(&self) -> Arc<dyn RelationTypeManager + Send + Sync> {
254 self.type_system.get_relation_type_manager()
255 }
256
257 fn get_relation_type_import_export_manager(&self) -> Arc<dyn RelationTypeImportExportManager + Send + Sync> {
258 self.type_system.get_relation_type_import_export_manager()
259 }
260
261 fn get_relation_type_provider_registry(&self) -> Arc<dyn RelationTypeProviderRegistry + Send + Sync> {
262 self.type_system.get_relation_type_provider_registry()
263 }
264
265 fn get_type_system_event_manager(&self) -> Arc<dyn TypeSystemEventManager + Send + Sync> {
266 self.type_system.get_type_system_event_manager()
267 }
268}
269
270impl ReactiveSystem for RuntimeImpl {
271 fn get_reactive_entity_manager(&self) -> Arc<dyn ReactiveEntityManager + Send + Sync> {
272 self.reactive_system.get_reactive_entity_manager()
273 }
274
275 fn get_reactive_flow_manager(&self) -> Arc<dyn ReactiveFlowManager + Send + Sync> {
276 self.reactive_system.get_reactive_flow_manager()
277 }
278
279 fn get_reactive_relation_manager(&self) -> Arc<dyn ReactiveRelationManager + Send + Sync> {
280 self.reactive_system.get_reactive_relation_manager()
281 }
282
283 fn get_reactive_instance_event_manager(&self) -> Arc<dyn ReactiveInstanceEventManager + Send + Sync> {
284 self.reactive_system.get_reactive_instance_event_manager()
285 }
286
287 fn type_system(&self) -> Arc<dyn TypeSystem + Send + Sync> {
288 self.reactive_system.type_system()
289 }
290
291 fn behaviour_system(&self) -> Arc<dyn BehaviourSystem + Send + Sync> {
292 self.reactive_system.behaviour_system()
293 }
294}
295
296impl BehaviourSystem for RuntimeImpl {
297 fn get_entity_behaviour_manager(&self) -> Arc<dyn EntityBehaviourManager + Send + Sync> {
298 self.behaviour_system.get_entity_behaviour_manager()
299 }
300
301 fn get_entity_behaviour_registry(&self) -> Arc<dyn EntityBehaviourRegistry + Send + Sync> {
302 self.behaviour_system.get_entity_behaviour_registry()
303 }
304
305 fn get_entity_component_behaviour_manager(&self) -> Arc<dyn EntityComponentBehaviourManager + Send + Sync> {
306 self.behaviour_system.get_entity_component_behaviour_manager()
307 }
308
309 fn get_entity_component_behaviour_registry(&self) -> Arc<dyn EntityComponentBehaviourRegistry + Send + Sync> {
310 self.behaviour_system.get_entity_component_behaviour_registry()
311 }
312
313 fn get_relation_behaviour_manager(&self) -> Arc<dyn RelationBehaviourManager + Send + Sync> {
314 self.behaviour_system.get_relation_behaviour_manager()
315 }
316
317 fn get_relation_behaviour_registry(&self) -> Arc<dyn RelationBehaviourRegistry + Send + Sync> {
318 self.behaviour_system.get_relation_behaviour_registry()
319 }
320
321 fn get_relation_component_behaviour_manager(&self) -> Arc<dyn RelationComponentBehaviourManager + Send + Sync> {
322 self.behaviour_system.get_relation_component_behaviour_manager()
323 }
324
325 fn get_relation_component_behaviour_registry(&self) -> Arc<dyn RelationComponentBehaviourRegistry + Send + Sync> {
326 self.behaviour_system.get_relation_component_behaviour_registry()
327 }
328
329 fn type_system(&self) -> Arc<dyn TypeSystem + Send + Sync> {
330 self.behaviour_system.type_system()
331 }
332}
333
334impl InstanceSystem for RuntimeImpl {
335 fn get_entity_instance_import_export_manager(&self) -> Arc<dyn EntityInstanceImportExportManager + Send + Sync> {
336 self.instance_system.get_entity_instance_import_export_manager()
337 }
338
339 fn get_relation_instance_import_export_manager(&self) -> Arc<dyn RelationInstanceImportExportManager + Send + Sync> {
340 self.instance_system.get_relation_instance_import_export_manager()
341 }
342
343 fn reactive_system(&self) -> Arc<dyn ReactiveSystem + Send + Sync> {
344 self.reactive_system.clone()
345 }
346}
347
348impl CommandSystem for RuntimeImpl {
349 fn get_command_manager(&self) -> Arc<dyn CommandManager + Send + Sync> {
350 self.command_system.get_command_manager()
351 }
352
353 fn get_command_type_provider(&self) -> Arc<dyn CommandTypeProvider + Send + Sync> {
354 self.command_system.get_command_type_provider()
355 }
356
357 fn type_system(&self) -> Arc<dyn TypeSystem + Send + Sync> {
358 self.command_system.type_system()
359 }
360
361 fn reactive_system(&self) -> Arc<dyn ReactiveSystem + Send + Sync> {
362 self.command_system.reactive_system()
363 }
364}
365
366impl ConfigSystem for RuntimeImpl {
367 fn get_config_manager(&self) -> Arc<dyn ConfigManager + Send + Sync> {
368 self.config_system.get_config_manager()
369 }
370}
371
372impl RemotesSystem for RuntimeImpl {
373 fn get_instance_service(&self) -> Arc<dyn InstanceService + Send + Sync> {
374 self.remotes_system.get_instance_service()
375 }
376
377 fn get_remotes_manager(&self) -> Arc<dyn RemotesManager + Send + Sync> {
378 self.remotes_system.get_remotes_manager()
379 }
380
381 fn config_system(&self) -> Arc<dyn ConfigSystem + Send + Sync> {
382 self.remotes_system.config_system()
383 }
384}
385
386impl RuntimeSystem for RuntimeImpl {
387 fn get_shutdown_manager(&self) -> Arc<dyn ShutdownManager + Send + Sync> {
388 self.runtime_system.get_shutdown_manager()
389 }
390}
391
392impl WebSystem for RuntimeImpl {
393 fn get_graphql_server(&self) -> Arc<dyn GraphQLServer + Send + Sync> {
394 self.web_system.get_graphql_server()
395 }
396
397 fn get_web_resource_manager(&self) -> Arc<dyn WebResourceManager + Send + Sync> {
398 self.web_system.get_web_resource_manager()
399 }
400
401 fn type_system(&self) -> Arc<dyn TypeSystem + Send + Sync> {
402 self.web_system.type_system()
403 }
404
405 fn reactive_system(&self) -> Arc<dyn ReactiveSystem + Send + Sync> {
406 self.web_system.reactive_system()
407 }
408
409 fn config_system(&self) -> Arc<dyn ConfigSystem + Send + Sync> {
410 self.web_system.config_system()
411 }
412
413 fn runtime_graphql_system(&self) -> Arc<dyn RuntimeGraphQLSystem + Send + Sync> {
414 self.web_system.runtime_graphql_system()
415 }
416
417 fn plugin_graphql_system(&self) -> Arc<dyn PluginGraphQLSystem + Send + Sync> {
418 self.web_system.plugin_graphql_system()
419 }
420
421 fn dynamic_graph_system(&self) -> Arc<dyn DynamicGraphSystem + Send + Sync> {
422 self.web_system.dynamic_graph_system()
423 }
424
425 fn graphql_system(&self) -> Arc<dyn GraphQLSystem + Send + Sync> {
426 self.web_system.graphql_system()
427 }
428}
429
430impl GraphQLSystem for RuntimeImpl {
431 fn get_graphql_query_service(&self) -> Arc<dyn GraphQLQueryService + Send + Sync> {
432 self.graphql_system.get_graphql_query_service()
433 }
434
435 fn get_graphql_schema_manager(&self) -> Arc<dyn GraphQLSchemaManager + Send + Sync> {
436 self.graphql_system.get_graphql_schema_manager()
437 }
438}
439
440impl DynamicGraphSystem for RuntimeImpl {
441 fn get_dynamic_graph_query_service(&self) -> Arc<dyn DynamicGraphQueryService + Send + Sync> {
442 self.dynamic_graph_system.get_dynamic_graph_query_service()
443 }
444
445 fn get_dynamic_graph_schema_manager(&self) -> Arc<dyn DynamicGraphSchemaManager + Send + Sync> {
446 self.dynamic_graph_system.get_dynamic_graph_schema_manager()
447 }
448
449 fn type_system(&self) -> Arc<dyn TypeSystem + Send + Sync> {
450 self.dynamic_graph_system.type_system()
451 }
452
453 fn reactive_system(&self) -> Arc<dyn ReactiveSystem + Send + Sync> {
454 self.dynamic_graph_system.reactive_system()
455 }
456}
457
458impl RuntimeGraphQLSystem for RuntimeImpl {
459 fn get_runtime_query_service(&self) -> Arc<dyn RuntimeQueryService + Send + Sync> {
460 self.runtime_graphql_system.get_runtime_query_service()
461 }
462
463 fn get_runtime_schema_manager(&self) -> Arc<dyn RuntimeSchemaManager + Send + Sync> {
464 self.runtime_graphql_system.get_runtime_schema_manager()
465 }
466}
467
468impl PluginGraphQLSystem for RuntimeImpl {
469 fn get_plugin_query_service(&self) -> Arc<dyn PluginQueryService + Send + Sync> {
470 self.plugin_graphql_system.get_plugin_query_service()
471 }
472
473 fn get_plugin_schema_manager(&self) -> Arc<dyn PluginSchemaManager + Send + Sync> {
474 self.plugin_graphql_system.get_plugin_schema_manager()
475 }
476}
477
478impl PluginSystem for RuntimeImpl {
479 fn get_plugin_context_factory(&self) -> Arc<dyn PluginContextFactory + Send + Sync> {
480 self.plugin_system.get_plugin_context_factory()
481 }
482
483 fn get_plugin_container_manager(&self) -> Arc<dyn PluginContainerManager + Send + Sync> {
484 self.plugin_system.get_plugin_container_manager()
485 }
486
487 fn get_plugin_repository_manager(&self) -> Arc<dyn PluginRepositoryManager + Send + Sync> {
488 self.plugin_system.get_plugin_repository_manager()
489 }
490
491 fn get_plugin_resolver(&self) -> Arc<dyn PluginResolver + Send + Sync> {
492 self.plugin_system.get_plugin_resolver()
493 }
494}
495
496#[async_trait]
497impl Lifecycle for RuntimeImpl {
498 async fn init(&self) {
499 self.type_system.init().await;
501 self.reactive_system.init().await;
502 self.behaviour_system.init().await;
503 self.instance_system.init().await;
504 self.command_system.init().await;
506 self.runtime_system.init().await;
507 self.remotes_system.init().await;
509 self.graphql_system.init().await;
511 self.dynamic_graph_system.init().await;
512 self.web_system.init().await;
514 self.plugin_system.init().await;
518 }
519
520 async fn post_init(&self) {
521 self.type_system.post_init().await;
523 self.reactive_system.post_init().await;
524 self.behaviour_system.post_init().await;
525 self.instance_system.post_init().await;
526 self.command_system.post_init().await;
528 self.runtime_system.post_init().await;
530 self.remotes_system.post_init().await;
531 self.graphql_system.post_init().await;
533 self.dynamic_graph_system.post_init().await;
534 self.web_system.post_init().await;
536 self.plugin_system.post_init().await;
540 }
541
542 async fn pre_shutdown(&self) {
543 self.plugin_system.pre_shutdown().await;
545 self.web_system.pre_shutdown().await;
549 self.dynamic_graph_system.pre_shutdown().await;
551 self.graphql_system.pre_shutdown().await;
552 self.remotes_system.pre_shutdown().await;
554 self.runtime_system.pre_shutdown().await;
556 self.command_system.init().await;
557 self.instance_system.pre_shutdown().await;
559 self.behaviour_system.pre_shutdown().await;
560 self.reactive_system.pre_shutdown().await;
561 self.type_system.pre_shutdown().await;
562 }
563
564 async fn shutdown(&self) {
565 self.plugin_system.shutdown().await;
567 self.web_system.shutdown().await;
571 self.dynamic_graph_system.shutdown().await;
573 self.graphql_system.shutdown().await;
574 self.remotes_system.shutdown().await;
576 self.runtime_system.shutdown().await;
578 self.command_system.init().await;
579 self.instance_system.shutdown().await;
581 self.behaviour_system.shutdown().await;
582 self.reactive_system.shutdown().await;
583 self.type_system.shutdown().await;
584 }
585}
586
587#[cfg(test)]
588mod tests {
589 use std::time::Duration;
590
591 use log::LevelFilter;
592 use log4rs::Config;
593 use log4rs::append::console::ConsoleAppender;
594 use log4rs::config::Appender;
595 use log4rs::config::Root;
596
597 use crate::get_runtime;
598
599 #[tokio::test(flavor = "multi_thread")]
604 async fn test_run() {
605 let stdout = ConsoleAppender::builder().build();
606 let config = Config::builder()
607 .appender(Appender::builder().build("stdout", Box::new(stdout)))
608 .build(Root::builder().appender("stdout").build(LevelFilter::Trace))
609 .expect("Failed to create logger");
610 if let Err(error) = log4rs::init_config(config) {
611 eprintln!("Failed to configure logger: {}", error);
612 }
613 let rt = get_runtime();
614 let runtime = rt.clone();
615 tokio::spawn(async move {
616 let runtime = runtime;
617 runtime.init().await;
618 runtime.post_init().await;
619 runtime.run().await;
620 runtime.pre_shutdown().await;
621 runtime.shutdown().await;
622 });
623 tokio::time::sleep(Duration::from_secs(2)).await;
624 rt.stop();
625 }
626}