1#[cfg(feature = "shutdown-hooks")]
2use super::shutdown::{normalize_shutdown_signals, wait_for_shutdown_signal, ShutdownSignal};
3use super::BootApplication;
4use crate::{BoxFuture, HttpAdapter, MessageTransport, ModuleRef, Result};
5#[cfg(feature = "shutdown-hooks")]
6use std::future::Future;
7use std::net::SocketAddr;
8use std::sync::Arc;
9
10pub struct BootApplicationHandle {
12 app: BootApplication,
13 initialized: bool,
14 microservices: Vec<Box<dyn ConnectedMicroservice>>,
15 #[cfg(feature = "shutdown-hooks")]
16 shutdown_signals: Option<Vec<ShutdownSignal>>,
17}
18
19impl BootApplicationHandle {
20 pub fn from_app(app: BootApplication) -> Self {
21 Self {
22 app,
23 initialized: false,
24 microservices: Vec::new(),
25 #[cfg(feature = "shutdown-hooks")]
26 shutdown_signals: None,
27 }
28 }
29
30 pub fn app(&self) -> &BootApplication {
31 &self.app
32 }
33
34 pub fn module_ref(&self) -> &ModuleRef {
35 self.app.module_ref()
36 }
37
38 pub fn into_app(self) -> BootApplication {
39 self.app
40 }
41
42 pub fn get<T>(&self) -> Result<Arc<T>>
43 where
44 T: Send + Sync + 'static,
45 {
46 self.app.get::<T>()
47 }
48
49 pub fn get_named<T>(&self, token: &str) -> Result<Arc<T>>
50 where
51 T: Send + Sync + 'static,
52 {
53 self.app.get_named::<T>(token)
54 }
55
56 pub fn get_optional<T>(&self) -> Result<Option<Arc<T>>>
57 where
58 T: Send + Sync + 'static,
59 {
60 self.app.get_optional::<T>()
61 }
62
63 pub fn get_optional_named<T>(&self, token: &str) -> Result<Option<Arc<T>>>
64 where
65 T: Send + Sync + 'static,
66 {
67 self.app.get_optional_named::<T>(token)
68 }
69
70 pub fn is_initialized(&self) -> bool {
71 self.initialized
72 }
73
74 #[cfg(feature = "shutdown-hooks")]
75 pub fn enable_shutdown_hooks<I>(&mut self, signals: I) -> &mut Self
76 where
77 I: IntoIterator<Item = ShutdownSignal>,
78 {
79 self.shutdown_signals = Some(normalize_shutdown_signals(signals));
80 self
81 }
82
83 #[cfg(feature = "shutdown-hooks")]
84 pub fn enable_default_shutdown_hooks(&mut self) -> &mut Self {
85 self.enable_shutdown_hooks(ShutdownSignal::default_signals())
86 }
87
88 pub async fn init(&mut self) -> Result<()> {
89 if self.initialized {
90 return Ok(());
91 }
92
93 if let Err(error) = self.app.bootstrap().await {
94 let _ = self.app.shutdown().await;
95 return Err(error);
96 }
97
98 self.initialized = true;
99 Ok(())
100 }
101
102 pub async fn close(&mut self) -> Result<()> {
103 self.close_inner(None).await
104 }
105
106 pub async fn close_with_signal(&mut self, signal: impl Into<String>) -> Result<()> {
107 self.close_inner(Some(signal.into())).await
108 }
109
110 async fn close_inner(&mut self, signal: Option<String>) -> Result<()> {
111 if !self.initialized {
112 return Ok(());
113 }
114
115 match signal {
116 Some(signal) => self.app.shutdown_with_signal(signal).await?,
117 None => self.app.shutdown().await?,
118 }
119 self.initialized = false;
120 Ok(())
121 }
122
123 pub async fn listen_with<A>(&mut self, adapter: &A, addr: SocketAddr) -> Result<()>
124 where
125 A: HttpAdapter,
126 {
127 #[cfg(feature = "shutdown-hooks")]
128 if let Some(signals) = self.shutdown_signals.clone() {
129 return self
130 .listen_with_shutdown_signal_future(
131 adapter,
132 addr,
133 wait_for_shutdown_signal(signals),
134 )
135 .await;
136 }
137
138 self.init().await?;
139 let serve_result = adapter.serve(self.app.clone(), addr).await;
140 let close_result = self.close().await;
141
142 match (serve_result, close_result) {
143 (Err(error), _) => Err(error),
144 (Ok(()), Err(error)) => Err(error),
145 (Ok(()), Ok(())) => Ok(()),
146 }
147 }
148
149 #[cfg(feature = "shutdown-hooks")]
150 pub(crate) async fn listen_with_shutdown_signal_future<A, F>(
151 &mut self,
152 adapter: &A,
153 addr: SocketAddr,
154 signal_future: F,
155 ) -> Result<()>
156 where
157 A: HttpAdapter,
158 F: Future<Output = Result<ShutdownSignal>> + Send,
159 {
160 self.init().await?;
161 let serve_future = adapter.serve(self.app.clone(), addr);
162 futures_util::pin_mut!(signal_future);
163 futures_util::pin_mut!(serve_future);
164
165 tokio::select! {
166 serve_result = &mut serve_future => {
167 let close_result = self.close().await;
168 match (serve_result, close_result) {
169 (Err(error), _) => Err(error),
170 (Ok(()), Err(error)) => Err(error),
171 (Ok(()), Ok(())) => Ok(()),
172 }
173 }
174 signal_result = &mut signal_future => {
175 match signal_result {
176 Ok(signal) => self.close_with_signal(signal.as_str()).await,
177 Err(error) => {
178 let _ = self.close().await;
179 Err(error)
180 }
181 }
182 }
183 }
184 }
185
186 pub fn connect_microservice<T>(&mut self, transport: T) -> usize
187 where
188 T: MessageTransport + Send + Sync + 'static,
189 {
190 let index = self.microservices.len();
191 self.microservices
192 .push(Box::new(ConnectedMessageTransport { transport }));
193 index
194 }
195
196 pub fn connected_microservice_count(&self) -> usize {
197 self.microservices.len()
198 }
199
200 pub async fn start_all_microservices(&mut self) -> Result<()> {
201 self.init().await?;
202 for microservice in &self.microservices {
203 microservice.serve(self.app.clone()).await?;
204 }
205 Ok(())
206 }
207}
208
209trait ConnectedMicroservice: Send + Sync {
210 fn serve(&self, app: BootApplication) -> BoxFuture<'static, Result<()>>;
211}
212
213struct ConnectedMessageTransport<T> {
214 transport: T,
215}
216
217impl<T> ConnectedMicroservice for ConnectedMessageTransport<T>
218where
219 T: MessageTransport + Send + Sync + 'static,
220{
221 fn serve(&self, app: BootApplication) -> BoxFuture<'static, Result<()>> {
222 self.transport.serve(app)
223 }
224}