bbox_map_server/
service.rs

1use crate::config::MapServiceCfg;
2use crate::fcgi_process::FcgiDispatcher;
3use crate::inventory::Inventory;
4use crate::metrics::{register_metrics, wms_metrics, WmsMetrics};
5use crate::wms_fcgi_backend::detect_backends;
6use actix_web::web;
7use async_trait::async_trait;
8use bbox_core::cli::{NoArgs, NoCommands};
9use bbox_core::config::CoreServiceCfg;
10use bbox_core::service::OgcApiService;
11use log::error;
12use prometheus::Registry;
13use std::collections::HashMap;
14
15#[derive(Clone)]
16pub struct MapService {
17    // Dispatcher is not Clone, so we wrap as web::Data already here
18    pub(crate) fcgi_clients: Vec<web::Data<FcgiDispatcher>>,
19    /// client index for each suffix
20    #[allow(dead_code)]
21    suffix_fcgi: HashMap<String, usize>,
22    /// Number of FCGI processes per backend
23    pub(crate) num_fcgi_processes: usize,
24    pub default_project: Option<String>,
25    pub(crate) inventory: Inventory,
26}
27
28#[async_trait]
29impl OgcApiService for MapService {
30    type Config = MapServiceCfg;
31    type CliCommands = NoCommands;
32    type CliArgs = NoArgs;
33    type Metrics = WmsMetrics;
34
35    async fn create(config: &Self::Config, core_cfg: &CoreServiceCfg) -> Self {
36        let loglevel = core_cfg.loglevel();
37        let num_fcgi_processes = config.num_fcgi_processes();
38        let default_project = config.default_project.clone();
39        let (process_pools, inventory) = detect_backends(config, &loglevel).unwrap();
40        let fcgi_clients = process_pools
41            .iter()
42            .map(|process_pool| web::Data::new(process_pool.client_dispatcher(config)))
43            .collect::<Vec<_>>();
44        let mut suffix_fcgi = HashMap::new();
45        for (poolno, fcgi_pool) in process_pools.iter().enumerate() {
46            for suffix_url in &fcgi_pool.suffixes {
47                suffix_fcgi.insert(suffix_url.suffix.clone(), poolno);
48            }
49        }
50
51        for mut process_pool in process_pools {
52            match process_pool.spawn_processes().await {
53                Ok(_) => {
54                    actix_web::rt::spawn(async move {
55                        process_pool.watchdog_loop().await;
56                    });
57                }
58                Err(e) => {
59                    error!("Spawn error: {e}");
60                }
61            }
62        }
63        // FIXME: Wait until FCGI services are started
64        tokio::time::sleep(std::time::Duration::from_secs(2)).await;
65
66        MapService {
67            fcgi_clients,
68            suffix_fcgi,
69            num_fcgi_processes,
70            default_project,
71            inventory,
72        }
73    }
74    fn conformance_classes(&self) -> Vec<String> {
75        vec![
76            // Core
77            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/core".to_string(),
78            /*
79            // Map Tilesets
80            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/tilesets".to_string(),
81            // Background
82            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/background".to_string(),
83            // Collection Selection
84            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/collections-selection".to_string(),
85            // Scaling
86            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/scaling".to_string(),
87            // Display Resolution
88            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/display-resolution".to_string(),
89            // Spatial subsetting
90            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/spatial-subsetting".to_string(),
91            // Date and Time
92            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/datetime".to_string(),
93            // General Subsetting
94            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/general-subsetting".to_string(),
95            // Coordinate Reference System
96            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/crs".to_string(),
97            // Custom Projection CRS
98            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/projection".to_string(),
99            // Collection Maps
100            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/collection-map".to_string(),
101            // Dataset Maps
102            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/dataset-map".to_string(),
103            // Styled Maps
104            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/styled-map".to_string(),
105            */
106            // PNG
107            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/png".to_string(),
108            // JPEG
109            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/jpeg".to_string(),
110            // TIFF
111            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/tiff".to_string(),
112            /*
113            // SVG
114            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/svg".to_string(),
115            // HTML
116            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/html".to_string(),
117            */
118            // OpenAPI Specification
119            "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/oas30".to_string(),
120        ]
121    }
122    fn openapi_yaml(&self) -> Option<&str> {
123        Some(include_str!("openapi.yaml"))
124    }
125    fn add_metrics(&self, prometheus: &Registry) {
126        register_metrics(prometheus, self.metrics());
127    }
128    fn metrics(&self) -> &'static Self::Metrics {
129        wms_metrics(self.num_fcgi_processes)
130    }
131}
132
133impl MapService {
134    #[allow(dead_code)]
135    pub fn fcgi_dispatcher(&self, suffix: &str) -> Option<&FcgiDispatcher> {
136        self.suffix_fcgi
137            .get(suffix)
138            .map(|no| self.fcgi_clients[*no].get_ref())
139    }
140}