use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, RwLock};
use std::time::*;
use lang_extension::any::*;
use lang_extension::fmt::*;
use super::*;
use crate::property::default::*;
#[derive(Clone)]
pub struct DefaultConfigurationManagerConfig {
name: String,
sources: Arc<Vec<Box<dyn ConfigurationSource>>>,
task_executor: ConsumerRef<Box<dyn Fn()>>,
}
impl PartialEq for DefaultConfigurationManagerConfig {
fn eq(&self, other: &Self) -> bool {
self.sources
.as_ref()
.reference_equals(other.sources.as_ref())
}
}
impl Eq for DefaultConfigurationManagerConfig {}
impl fmt::Debug for DefaultConfigurationManagerConfig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{} {{\n\tname: {},\n\tsources: {:?},\n\ttask_executor:\n\t{}\n}}",
self.type_name(),
self.name,
self.sources,
self.task_executor.as_ref().to_instance_string()
)
}
}
unsafe impl Sync for DefaultConfigurationManagerConfig {}
unsafe impl Send for DefaultConfigurationManagerConfig {}
impl ConfigurationManagerConfig for DefaultConfigurationManagerConfig {
fn get_name(&self) -> &str {
self.name.as_str()
}
fn get_sources(&self) -> &Vec<Box<dyn ConfigurationSource>> {
self.sources.as_ref()
}
fn get_task_executor(&self) -> &dyn Fn(&Box<dyn Fn()>) {
self.task_executor.as_ref().as_ref()
}
as_boxed!(impl ConfigurationManagerConfig);
}
pub struct DefaultConfigurationManagerConfigBuilder {
name: Option<String>,
sources: HashMap<i32, Box<dyn ConfigurationSource>>,
task_executor: Option<ConsumerRef<Box<dyn Fn()>>>,
}
impl DefaultConfigurationManagerConfigBuilder {
pub fn new() -> Self {
Self {
name: None,
sources: HashMap::new(),
task_executor: Some(arc_boxed!(Self::execute)),
}
}
fn execute(action: &Box<dyn Fn()>) {
action()
}
}
impl ConfigurationManagerConfigBuilder for DefaultConfigurationManagerConfigBuilder {
fn set_name(&mut self, name: &str) -> &mut dyn ConfigurationManagerConfigBuilder {
self.name = Some(name.to_owned());
self
}
fn add_source(
&mut self,
priority: i32,
source: Box<dyn ConfigurationSource>,
) -> &mut dyn ConfigurationManagerConfigBuilder {
if self.sources.contains_key(&priority) {
panic!("a source with priority {} exists", priority);
}
self.sources.insert(priority, source);
self
}
fn add_sources(
&mut self,
sources: HashMap<i32, Box<dyn ConfigurationSource>>,
) -> &mut dyn ConfigurationManagerConfigBuilder {
for (k, v) in sources.into_iter() {
self.sources.insert(k, v);
}
self
}
fn set_task_executor(
&mut self,
task_executor: ConsumerRef<Box<dyn Fn()>>,
) -> &mut dyn ConfigurationManagerConfigBuilder {
self.task_executor = Some(task_executor);
self
}
fn build(&self) -> Box<dyn ConfigurationManagerConfig> {
Box::new(DefaultConfigurationManagerConfig {
name: self.name.as_ref().unwrap().to_owned(),
sources: Arc::new({
let mut keys: Vec<_> = self
.sources
.keys()
.collect::<Vec<&i32>>()
.iter()
.map(|v| **v)
.collect();
keys.sort();
keys.reverse();
let mut sources = Vec::new();
for k in keys {
sources.push(ConfigurationSource::clone_boxed(
self.sources.get(&k).unwrap().as_ref(),
));
}
sources
}),
task_executor: self.task_executor.as_ref().unwrap().clone(),
})
}
}
#[derive(Clone)]
pub struct DefaultConfigurationManager {
config: Arc<Box<dyn ConfigurationManagerConfig>>,
properties: Arc<RwLock<HashMap<ImmutableKey, Box<dyn RawProperty>>>>,
change_listeners: Arc<RwLock<Vec<RawPropertyChangeListener>>>,
}
impl DefaultConfigurationManager {
pub fn new(config: Box<dyn ConfigurationManagerConfig>) -> DefaultConfigurationManager {
let manager = DefaultConfigurationManager {
config: Arc::new(config),
properties: Arc::new(RwLock::new(HashMap::new())),
change_listeners: Arc::new(RwLock::new(Vec::new())),
};
let clone = manager.clone();
let source_change_listener: ConfigurationSourceChangeListener =
Arc::new(Box::new(move |e| clone.on_source_change(e)));
for s in manager.config.get_sources() {
s.add_change_listener(source_change_listener.clone());
}
manager
}
fn on_source_change(&self, _event: &dyn ConfigurationSourceChangeEvent) {
let properties = self.properties.read().unwrap();
for property in properties.values() {
let (new_value, source) = self.do_get_property_value(property.get_raw_config());
let old_value = property.get_raw_value();
if new_value != old_value {
if property.get_raw_config().is_static() {
warn!(
"ignore dynamic change for static property, \
dynamic change for static property will be applied when app restart, \
static:\n\t{:?},\n\tdynamic:\n\t{:?},\n\tproperty: {:?}",
old_value,
new_value,
property.get_raw_config().get_raw_key()
);
continue;
}
if new_value.is_none() && property.get_raw_config().is_required() {
error!(
"ignore dynamic change for required property, \
required property cannot be changed to None, \
now keep its current value, you should fix the invalid change at once, \
or app will panic when next app restart, property:\n\t{:?}",
property.get_raw_config().get_raw_key()
);
continue;
}
let pe = DefaultRawPropertyChangeEvent::new(
Arc::new(RawProperty::clone_boxed(property.as_ref())),
old_value.map(|v| ImmutableValue::wrap(v)),
new_value.as_ref().map(|v| ImmutableValue::wrap(v.clone())),
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis(),
);
let raw_property = property.as_ref();
let default_raw_property = raw_property
.as_any_ref()
.downcast_ref::<DefaultRawProperty>()
.unwrap()
.clone();
default_raw_property.update(new_value, source);
let pe_clone = pe.clone();
let mut action: Box<dyn Fn()> =
Box::new(move || default_raw_property.raise_change_event(&pe_clone));
self.config.get_task_executor()(&action);
let manager = self.clone();
action = Box::new(move || manager.raise_change_event(&pe));
self.config.get_task_executor()(&action);
}
}
}
fn do_get_property_value(
&self,
config: &dyn RawPropertyConfig,
) -> (Option<Box<dyn Value>>, Option<Box<dyn ConfigurationSource>>) {
for source in self.config.get_sources().iter() {
let value = source.get_property_value(config);
let new_value = self.apply_value_filter(config, source.get_config().get_name(), value);
if new_value.is_some() {
return (new_value, Some(source.clone()));
}
}
(config.get_raw_default_value(), None)
}
fn apply_value_filter(
&self,
config: &dyn RawPropertyConfig,
source: &str,
value: Option<Box<dyn Value>>,
) -> Option<Box<dyn Value>> {
if value.is_none() {
return None;
}
if config.get_value_filter().is_none() {
return value;
}
let new_value = config
.get_value_filter()
.unwrap()
.filter_raw(value.clone().unwrap());
if new_value.is_some() {
if new_value != value {
warn!(
"property value in config source {:?}\n\tchanged by property filter, \
from: {:?},\n\tto: {:?},\n\tproperty: {:?}",
source,
value.as_ref().unwrap(),
new_value.as_ref().unwrap(),
config
);
}
info!(
"use property value in config source {:?},\n\tvalue: {:?},\n\tproperty: {:?}",
source,
new_value.as_ref().unwrap(),
config.get_raw_key()
);
return new_value;
} else {
error!(
"property value in source {:?}\n\tignored by property filter, probably not valid, \
value: {:?},\n\tproperty: {:?}",
source,
value.unwrap(),
config
);
None
}
}
fn check_required(&self, config: &dyn RawPropertyConfig, value: Option<&Box<dyn Value>>) {
if config.is_required() && value.is_none() {
panic!(
"None is got for required property, forgot to config it?\n\
Property: {:?}",
config
);
}
}
fn raise_change_event(&self, event: &dyn RawPropertyChangeEvent) {
let listeners = self.change_listeners.read().unwrap();
for listener in listeners.iter() {
listener(event);
}
}
}
impl ConfigurationManager for DefaultConfigurationManager {
fn get_config(&self) -> &dyn ConfigurationManagerConfig {
self.config.as_ref().as_ref()
}
fn get_properties(&self) -> Vec<Box<dyn RawProperty>> {
let lock = self.properties.read().unwrap();
let mut properties = Vec::<Box<dyn RawProperty>>::new();
for (_, p) in lock.iter() {
properties.push(p.clone());
}
properties
}
fn get_property(&self, config: &dyn RawPropertyConfig) -> Box<dyn RawProperty> {
let key = ImmutableKey::wrap(config.get_raw_key());
let mut opt_property = self
.properties
.read()
.unwrap()
.get(&key)
.map(|p| RawProperty::clone_boxed(p.as_ref()));
if opt_property.is_none() {
let mut map = self.properties.write().unwrap();
opt_property = match map.get(&key) {
Some(p) => Some(RawProperty::clone_boxed(p.as_ref())),
None => {
let (value, source) = self.do_get_property_value(config);
self.check_required(config, value.as_ref());
let property = DefaultRawProperty::new(config);
property.update(value, source);
map.insert(key.clone(), RawProperty::clone_boxed(&property));
Some(Box::new(property))
}
};
}
let property = opt_property.unwrap();
if !config.equals(property.get_raw_config().as_any_ref()) {
panic!(
"make sure using same config for property: {:?},\n\tprevious config: {:?},\n\t\
current config: {:?}",
config.get_raw_key(),
property.get_raw_config(),
config
);
}
property
}
fn get_property_value(&self, config: &dyn RawPropertyConfig) -> Option<Box<dyn Value>> {
let (value, _) = self.do_get_property_value(config);
self.check_required(config, value.as_ref());
value
}
fn add_raw_change_listener(&self, listener: RawPropertyChangeListener) {
self.change_listeners.write().unwrap().push(listener);
}
as_boxed!(impl ConfigurationManager);
}
impl PartialEq for DefaultConfigurationManager {
fn eq(&self, other: &Self) -> bool {
self.properties
.as_ref()
.reference_equals(other.properties.as_ref())
}
}
impl Eq for DefaultConfigurationManager {}
impl fmt::Debug for DefaultConfigurationManager {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{} {{\n\tconfig: {:?},\n\tproperties: {:?}\n}}",
self.type_name(),
self.get_config(),
self.get_properties()
)
}
}
unsafe impl Sync for DefaultConfigurationManager {}
unsafe impl Send for DefaultConfigurationManager {}
#[cfg(test)]
mod test {
use super::*;
use crate::source::default::*;
use crate::tests::init_log;
use lang_extension::convert::*;
use std::sync::atomic::*;
use std::thread;
fn new_source() -> DefaultConfigurationSource {
DefaultConfigurationSource::new(
DefaultConfigurationSourceConfigBuilder::new()
.set_name("test")
.build(),
Arc::new(Box::new(move |o| -> Option<Box<dyn Value>> {
let x = if Key::as_trait_ref(&"key_ok".to_string()).equals(o.as_any_ref()) {
Some(Value::to_boxed("10".to_string()))
} else if Key::as_trait_ref(&"key_error".to_string()).equals(o.as_any_ref()) {
Some(Value::to_boxed("error".to_string()))
} else {
None
};
println!("raw_value: {:?}", x);
x
})),
)
}
fn new_value_converter() -> DefaultTypeConverter<String, i32> {
DefaultTypeConverter::<String, i32>::new(Box::new(|s| match s.parse::<i32>() {
Ok(v) => {
println!("parse value: {}", v);
Ok(Box::new(v))
}
Err(e) => {
println!("parse error: {}", e);
Err(Box::new(e.to_string()))
}
}))
}
fn new_property_config(
value_converter: &dyn TypeConverter<String, i32>,
) -> Box<dyn PropertyConfig<String, i32>> {
DefaultPropertyConfigBuilder::<String, i32>::new()
.set_key(Box::new("key_ok".to_string()))
.add_value_converter(RawTypeConverter::clone_boxed(value_converter))
.set_value_filter(Box::new(DefaultValueFilter::<i32>::new(Box::new(|v| {
if *v == 10 { Some(Box::new(5)) } else { Some(v) }
}))))
.set_doc("for test")
.build()
}
fn new_property_config2(
value_converter: &dyn TypeConverter<String, i32>,
) -> Box<dyn PropertyConfig<String, i32>> {
DefaultPropertyConfigBuilder::<String, i32>::new()
.set_key(Box::new("key_error".to_string()))
.add_value_converter(RawTypeConverter::clone_boxed(value_converter))
.build()
}
fn new_property_config3(
key: &str,
value_converter: &dyn TypeConverter<String, i32>,
) -> Box<dyn PropertyConfig<String, i32>> {
DefaultPropertyConfigBuilder::<String, i32>::new()
.set_key(Box::new(key.to_string()))
.add_value_converter(RawTypeConverter::clone_boxed(value_converter))
.set_required(true)
.build()
}
fn new_property_config4(
key: &str,
value_converter: &dyn TypeConverter<String, i32>,
) -> Box<dyn PropertyConfig<String, i32>> {
DefaultPropertyConfigBuilder::<String, i32>::new()
.set_key(Box::new(key.to_string()))
.add_value_converter(RawTypeConverter::clone_boxed(value_converter))
.set_static(true)
.build()
}
#[test]
fn manager_config_test() {
let source = new_source();
let config = DefaultConfigurationManagerConfigBuilder::new()
.set_name("test")
.add_source(1, Box::new(source))
.build();
println!("manager config: {:?}", config);
assert_eq!("test", config.get_name());
assert_eq!(1, config.get_sources().len());
let action: Box<dyn Fn()> = Box::new(|| {
println!("task executed");
});
config.get_task_executor()(&action);
assert_eq!(&config, &config.clone());
}
#[test]
fn manager_test() {
init_log();
let source = ConfigurationSource::to_boxed(new_source());
let config = DefaultConfigurationManagerConfigBuilder::new()
.set_name("test")
.add_source(1, source.clone())
.build();
let manager = DefaultConfigurationManager::new(config);
println!("manager: {:?}", manager);
assert_eq!("test", manager.get_config().get_name());
let value_converter = new_value_converter();
let config = new_property_config(&value_converter);
let property = manager.get_property(RawPropertyConfig::as_trait_ref(config.as_ref()));
assert_eq!(Some(Value::to_boxed(5)), property.get_raw_value());
assert_eq!(Some(source.clone()), property.get_source());
let config2 = new_property_config2(&value_converter);
let property2 = manager.get_property(RawPropertyConfig::as_trait_ref(config2.as_ref()));
assert_eq!(None, property2.get_raw_value());
assert_eq!(None, property2.get_source());
let value = manager.get_property_value(RawPropertyConfig::as_trait_ref(config.as_ref()));
assert_eq!(Some(Value::to_boxed(5)), value);
let value2 = manager.get_property_value(RawPropertyConfig::as_trait_ref(config2.as_ref()));
assert_eq!(None, value2);
let manager_clone = manager.clone();
let config_clone = config.clone();
let handle = thread::spawn(move || {
let property =
manager_clone.get_property(RawPropertyConfig::as_trait_ref(config_clone.as_ref()));
assert_eq!(Some(Value::to_boxed(5)), property.get_raw_value());
});
handle.join().unwrap();
}
#[test]
fn dynamic_test() {
init_log();
let mut builder = DefaultConfigurationSourceConfigBuilder::new();
let source_config = builder.set_name("test").build();
let property_provider: PropertyProvider = Arc::new(Box::new(|k| {
if k.equals("key_ok".to_string().as_any_ref()) {
Some(Value::to_boxed(20))
} else {
None
}
}));
let source0: Box<dyn ConfigurationSource> = Box::new(DefaultConfigurationSource::new(
source_config,
property_provider,
));
let memory_map = Arc::new(RwLock::new(HashMap::<String, String>::new()));
memory_map
.write()
.unwrap()
.insert("key_ok".to_string(), "10".to_string());
memory_map
.write()
.unwrap()
.insert("key_error".to_string(), "error".to_string());
let memory_map2 = memory_map.clone();
let source = DefaultConfigurationSource::new(
DefaultConfigurationSourceConfigBuilder::new()
.set_name("test")
.build(),
Arc::new(Box::new(move |o| -> Option<Box<dyn Value>> {
match o.as_any_ref().downcast_ref::<String>() {
Some(k) => memory_map2
.read()
.unwrap()
.get(k)
.map(|v| Value::clone_boxed(v)),
None => None,
}
})),
);
let source_boxed = ConfigurationSource::clone_boxed(&source);
let config = DefaultConfigurationManagerConfigBuilder::new()
.set_name("test")
.add_source(0, source0.clone())
.add_source(1, source_boxed.clone())
.build();
let manager = DefaultConfigurationManager::new(config);
let value_converter = new_value_converter();
let config = new_property_config(&value_converter);
let property = manager.get_property(RawPropertyConfig::as_trait_ref(config.as_ref()));
assert_eq!(Some(Value::to_boxed(5)), property.get_raw_value());
assert_eq!(Some(source_boxed.clone()), property.get_source());
let config2 = new_property_config2(&value_converter);
let property2 = manager.get_property(RawPropertyConfig::as_trait_ref(config2.as_ref()));
assert_eq!(None, property2.get_raw_value());
assert_eq!(None, property2.get_source());
let value = manager.get_property_value(RawPropertyConfig::as_trait_ref(config.as_ref()));
assert_eq!(Some(Value::to_boxed(5)), value);
let value2 = manager.get_property_value(RawPropertyConfig::as_trait_ref(config2.as_ref()));
assert_eq!(None, value2);
memory_map
.write()
.unwrap()
.insert("key_ok".to_string(), "11".to_string());
source.raise_change_event();
assert_eq!(Some(Value::to_boxed(11)), property.get_raw_value());
let value2 = manager.get_property_value(RawPropertyConfig::as_trait_ref(config2.as_ref()));
assert_eq!(None, value2);
let value = manager.get_property_value(RawPropertyConfig::as_trait_ref(config.as_ref()));
assert_eq!(Some(Value::to_boxed(11)), value);
let value2 = manager.get_property_value(RawPropertyConfig::as_trait_ref(config2.as_ref()));
assert_eq!(None, value2);
let property = DefaultProperty::<String, i32>::from_raw(property.as_ref());
let changed = Arc::new(AtomicBool::new(false));
let changed_clone = changed.clone();
property.add_change_listener(Arc::new(Box::new(move |e| {
println!("changed: {:?}", e);
changed_clone.swap(true, Ordering::Relaxed);
})));
memory_map
.write()
.unwrap()
.insert("key_ok".to_string(), "12".to_string());
source.raise_change_event();
assert_eq!(Some(Box::new(12)), property.get_value());
assert_eq!(Some(source_boxed.clone()), property.get_source());
assert_eq!(Some(Value::to_boxed(12)), property.get_raw_value());
let value = manager.get_property_value(RawPropertyConfig::as_trait_ref(config.as_ref()));
assert_eq!(Some(Value::to_boxed(12)), value);
assert!(changed.fetch_and(true, Ordering::Relaxed));
memory_map.write().unwrap().remove(&"key_ok".to_string());
source.raise_change_event();
assert_eq!(Some(Box::new(20)), property.get_value());
assert_eq!(Some(source0.clone()), property.get_source());
memory_map
.write()
.unwrap()
.insert("key_error".to_string(), "1".to_string());
source.raise_change_event();
assert_eq!(Some(Value::to_boxed(1)), property2.get_raw_value());
}
#[test]
fn required_property() {
init_log();
let source = new_source();
let config = DefaultConfigurationManagerConfigBuilder::new()
.set_name("test")
.add_source(1, Box::new(source))
.build();
let manager = DefaultConfigurationManager::new(config);
let value_converter = new_value_converter();
let config = new_property_config3("key_ok", &value_converter);
manager.get_property(RawPropertyConfig::as_trait_ref(config.as_ref()));
manager.get_property_value(RawPropertyConfig::as_trait_ref(config.as_ref()));
}
#[should_panic]
#[test]
fn required_property2() {
init_log();
let source = new_source();
let config = DefaultConfigurationManagerConfigBuilder::new()
.set_name("test")
.add_source(1, Box::new(source))
.build();
let manager = DefaultConfigurationManager::new(config);
let value_converter = new_value_converter();
let config = new_property_config3("key_none", &value_converter);
manager.get_property(RawPropertyConfig::as_trait_ref(config.as_ref()));
}
#[should_panic]
#[test]
fn required_property3() {
init_log();
let source = new_source();
let config = DefaultConfigurationManagerConfigBuilder::new()
.set_name("test")
.add_source(1, Box::new(source))
.build();
let manager = DefaultConfigurationManager::new(config);
let value_converter = new_value_converter();
let config = new_property_config3("key_none", &value_converter);
manager.get_property_value(RawPropertyConfig::as_trait_ref(config.as_ref()));
}
#[test]
fn required_property_dynamic() {
init_log();
let memory_map = Arc::new(RwLock::new(HashMap::<String, String>::new()));
memory_map
.write()
.unwrap()
.insert("key_ok".to_string(), "20".to_string());
memory_map
.write()
.unwrap()
.insert("key_required".to_string(), "30".to_string());
let memory_map2 = memory_map.clone();
let source = DefaultConfigurationSource::new(
DefaultConfigurationSourceConfigBuilder::new()
.set_name("test")
.build(),
Arc::new(Box::new(move |o| -> Option<Box<dyn Value>> {
match o.as_any_ref().downcast_ref::<String>() {
Some(k) => memory_map2
.read()
.unwrap()
.get(k)
.map(|v| Value::clone_boxed(v)),
None => None,
}
})),
);
let config = DefaultConfigurationManagerConfigBuilder::new()
.set_name("test")
.add_source(1, Box::new(source.clone()))
.build();
let manager = DefaultConfigurationManager::new(config);
let value_converter = new_value_converter();
let config = new_property_config(&value_converter);
let property = manager.get_property(RawPropertyConfig::as_trait_ref(config.as_ref()));
assert_eq!(Some(Value::to_boxed(20)), property.get_raw_value());
memory_map.write().unwrap().remove(&"key_ok".to_string());
source.raise_change_event();
assert_eq!(None, property.get_raw_value());
let config = new_property_config3("key_required", &value_converter);
let property = manager.get_property(RawPropertyConfig::as_trait_ref(config.as_ref()));
assert_eq!(Some(Value::to_boxed(30)), property.get_raw_value());
memory_map
.write()
.unwrap()
.remove(&"key_required".to_string());
source.raise_change_event();
assert_eq!(Some(Value::to_boxed(30)), property.get_raw_value());
}
#[test]
fn static_property() {
init_log();
let memory_map = Arc::new(RwLock::new(HashMap::<String, String>::new()));
memory_map
.write()
.unwrap()
.insert("key_ok".to_string(), "20".to_string());
let memory_map2 = memory_map.clone();
let source = DefaultConfigurationSource::new(
DefaultConfigurationSourceConfigBuilder::new()
.set_name("test")
.build(),
Arc::new(Box::new(move |o| -> Option<Box<dyn Value>> {
match o.as_any_ref().downcast_ref::<String>() {
Some(k) => memory_map2
.read()
.unwrap()
.get(k)
.map(|v| Value::clone_boxed(v)),
None => None,
}
})),
);
let config = DefaultConfigurationManagerConfigBuilder::new()
.set_name("test")
.add_source(1, Box::new(source.clone()))
.build();
let manager = DefaultConfigurationManager::new(config);
let value_converter = new_value_converter();
let config = new_property_config4("key_ok", &value_converter);
let property = manager.get_property(RawPropertyConfig::as_trait_ref(config.as_ref()));
assert_eq!(Some(Value::to_boxed(20)), property.get_raw_value());
memory_map
.write()
.unwrap()
.insert("key_ok".to_string(), "30".to_string());
source.raise_change_event();
assert_eq!(Some(Value::to_boxed(20)), property.get_raw_value());
memory_map.write().unwrap().remove(&"key_ok".to_string());
source.raise_change_event();
assert_eq!(Some(Value::to_boxed(20)), property.get_raw_value());
}
}