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}