rocketmq_common/common/
config_manager.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License; Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing; software
12 * distributed under the License is distributed on an "AS IS" BASIS;
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND; either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17use std::any::Any;
18use std::collections::HashMap;
19
20use tracing::error;
21use tracing::info;
22use tracing::warn;
23
24use crate::FileUtils;
25
26// Define the trait ConfigManager
27pub trait ConfigManager {
28    /// Loads the configuration from a file.
29    ///
30    /// This method attempts to load the configuration from a file whose path is returned by
31    /// `config_file_path`. If the file content is empty, it attempts to load from a backup
32    /// file. If the file content is not empty, it decodes the content and logs a success
33    /// message.
34    ///
35    /// # Returns
36    /// * `true` if the configuration is successfully loaded and decoded.
37    /// * `false` if the configuration loading fails.
38    fn load(&self) -> bool {
39        let file_name = self.config_file_path();
40        let result = FileUtils::file_to_string(file_name.as_str());
41        match result {
42            Ok(ref content) => {
43                if content.is_empty() {
44                    warn!("load bak config file");
45                    self.load_bak()
46                } else {
47                    self.decode(content);
48                    info!("load Config file: {} -----OK", file_name);
49                    true
50                }
51            }
52            Err(_) => self.load_bak(),
53        }
54    }
55
56    /// Loads the configuration from a backup file.
57    ///
58    /// This method attempts to load the configuration from a backup file whose path is returned by
59    /// `config_file_path` with ".bak" appended. If the file content is not empty, it decodes
60    /// the content and logs a success message.
61    ///
62    /// # Returns
63    /// * `true` if the configuration is successfully loaded and decoded.
64    /// * `false` if the configuration loading fails.
65    fn load_bak(&self) -> bool {
66        let file_name = self.config_file_path();
67        if let Ok(ref content) =
68            FileUtils::file_to_string(format!("{}{}", file_name, ".bak").as_str())
69        {
70            if !content.is_empty() {
71                self.decode(content);
72                info!("load Config file: {}.bak -----OK", file_name);
73            }
74            true
75        } else {
76            error!("load Config file: {}.bak -----Failed", file_name);
77            false
78        }
79    }
80
81    /// Persists the configuration with a topic.
82    ///
83    /// This method persists the configuration with a given topic.
84    /// The actual implementation is delegated to the `persist` method.
85    fn persist_with_topic(&mut self, _topic_name: &str, _t: Box<dyn Any>) {
86        self.persist()
87    }
88
89    /// Persists the configuration with a map.
90    ///
91    /// This method persists the configuration with a given map.
92    /// The actual implementation is delegated to the `persist` method.
93    fn persist_map(&mut self, _m: &HashMap<String, Box<dyn Any>>) {
94        self.persist()
95    }
96
97    /// Persists the configuration.
98    ///
99    /// This method persists the configuration to a file whose path is returned by
100    /// `config_file_path`. If the encoded configuration is not empty, it writes the
101    /// configuration to the file.
102    fn persist(&self) {
103        let json = self.encode_pretty(true);
104        if !json.is_empty() {
105            let file_name = self.config_file_path();
106            if FileUtils::string_to_file(json.as_str(), file_name.as_str()).is_err() {
107                error!("persist file {} exception", file_name);
108            }
109        }
110    }
111
112    /// Decodes the configuration.
113    ///
114    /// This method is a placeholder for decoding the configuration.
115    /// The actual implementation should be provided by the implementer of the trait.
116    fn decode0(&mut self, _key: &[u8], _body: &[u8]) {
117        //nothing to do
118    }
119
120    /// Stops the configuration manager.
121    ///
122    /// This method is a placeholder for stopping the configuration manager.
123    /// The actual implementation should be provided by the implementer of the trait.
124    ///
125    /// # Returns
126    /// * `true` by default.
127    fn stop(&mut self) -> bool {
128        true
129    }
130
131    /// Returns the file path for the configuration file.
132    ///
133    /// This method should be implemented to return the path of the configuration file
134    /// that the `ConfigManager` will use to load or persist the configuration.
135    ///
136    /// # Returns
137    /// A `String` representing the path of the configuration file.
138    fn config_file_path(&self) -> String;
139
140    /// Encodes the current configuration into a `String`.
141    ///
142    /// This method leverages `encode_pretty` with `pretty_format` set to `false` to encode
143    /// the current configuration into a compact `String` representation.
144    ///
145    /// # Returns
146    /// A `String` representing the encoded configuration in a compact format.
147    fn encode(&self) -> String {
148        self.encode_pretty(false)
149    }
150
151    /// Encodes the current configuration into a `String` with an option for pretty formatting.
152    ///
153    /// This method encodes the current configuration into a `String`. It offers an option to
154    /// format the output in a more readable (pretty) format if `pretty_format` is `true`.
155    ///
156    /// # Arguments
157    /// * `pretty_format` - A boolean indicating whether the output should be pretty formatted.
158    ///
159    /// # Returns
160    /// A `String` representing the encoded configuration, optionally in a pretty format.
161    fn encode_pretty(&self, pretty_format: bool) -> String;
162
163    /// Decodes the configuration from a JSON string.
164    ///
165    /// This method takes a JSON string representation of the configuration and decodes it
166    /// into the internal representation used by the `ConfigManager`. Implementations should
167    /// update the internal state based on the provided JSON string.
168    ///
169    /// # Arguments
170    /// * `json_string` - A `&str` representing the configuration in JSON format.
171    fn decode(&self, json_string: &str);
172}