2
0

BaseConfigurationManager.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. using MediaBrowser.Common.Configuration;
  2. using MediaBrowser.Common.Events;
  3. using MediaBrowser.Model.Configuration;
  4. using MediaBrowser.Model.Logging;
  5. using MediaBrowser.Model.Serialization;
  6. using System;
  7. using System.Collections.Concurrent;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Threading;
  12. using CommonIO;
  13. namespace MediaBrowser.Common.Implementations.Configuration
  14. {
  15. /// <summary>
  16. /// Class BaseConfigurationManager
  17. /// </summary>
  18. public abstract class BaseConfigurationManager : IConfigurationManager
  19. {
  20. /// <summary>
  21. /// Gets the type of the configuration.
  22. /// </summary>
  23. /// <value>The type of the configuration.</value>
  24. protected abstract Type ConfigurationType { get; }
  25. /// <summary>
  26. /// Occurs when [configuration updated].
  27. /// </summary>
  28. public event EventHandler<EventArgs> ConfigurationUpdated;
  29. /// <summary>
  30. /// Occurs when [configuration updating].
  31. /// </summary>
  32. public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating;
  33. /// <summary>
  34. /// Occurs when [named configuration updated].
  35. /// </summary>
  36. public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
  37. /// <summary>
  38. /// Gets the logger.
  39. /// </summary>
  40. /// <value>The logger.</value>
  41. protected ILogger Logger { get; private set; }
  42. /// <summary>
  43. /// Gets the XML serializer.
  44. /// </summary>
  45. /// <value>The XML serializer.</value>
  46. protected IXmlSerializer XmlSerializer { get; private set; }
  47. /// <summary>
  48. /// Gets or sets the application paths.
  49. /// </summary>
  50. /// <value>The application paths.</value>
  51. public IApplicationPaths CommonApplicationPaths { get; private set; }
  52. public readonly IFileSystem FileSystem;
  53. /// <summary>
  54. /// The _configuration loaded
  55. /// </summary>
  56. private bool _configurationLoaded;
  57. /// <summary>
  58. /// The _configuration sync lock
  59. /// </summary>
  60. private object _configurationSyncLock = new object();
  61. /// <summary>
  62. /// The _configuration
  63. /// </summary>
  64. private BaseApplicationConfiguration _configuration;
  65. /// <summary>
  66. /// Gets the system configuration
  67. /// </summary>
  68. /// <value>The configuration.</value>
  69. public BaseApplicationConfiguration CommonConfiguration
  70. {
  71. get
  72. {
  73. // Lazy load
  74. LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer));
  75. return _configuration;
  76. }
  77. protected set
  78. {
  79. _configuration = value;
  80. _configurationLoaded = value != null;
  81. }
  82. }
  83. private ConfigurationStore[] _configurationStores = {};
  84. private IConfigurationFactory[] _configurationFactories = {};
  85. /// <summary>
  86. /// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class.
  87. /// </summary>
  88. /// <param name="applicationPaths">The application paths.</param>
  89. /// <param name="logManager">The log manager.</param>
  90. /// <param name="xmlSerializer">The XML serializer.</param>
  91. protected BaseConfigurationManager(IApplicationPaths applicationPaths, ILogManager logManager, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
  92. {
  93. CommonApplicationPaths = applicationPaths;
  94. XmlSerializer = xmlSerializer;
  95. FileSystem = fileSystem;
  96. Logger = logManager.GetLogger(GetType().Name);
  97. UpdateCachePath();
  98. }
  99. public virtual void AddParts(IEnumerable<IConfigurationFactory> factories)
  100. {
  101. _configurationFactories = factories.ToArray();
  102. _configurationStores = _configurationFactories
  103. .SelectMany(i => i.GetConfigurations())
  104. .ToArray();
  105. }
  106. /// <summary>
  107. /// Saves the configuration.
  108. /// </summary>
  109. public void SaveConfiguration()
  110. {
  111. var path = CommonApplicationPaths.SystemConfigurationFilePath;
  112. Directory.CreateDirectory(Path.GetDirectoryName(path));
  113. lock (_configurationSyncLock)
  114. {
  115. XmlSerializer.SerializeToFile(CommonConfiguration, path);
  116. }
  117. OnConfigurationUpdated();
  118. }
  119. /// <summary>
  120. /// Called when [configuration updated].
  121. /// </summary>
  122. protected virtual void OnConfigurationUpdated()
  123. {
  124. UpdateCachePath();
  125. EventHelper.QueueEventIfNotNull(ConfigurationUpdated, this, EventArgs.Empty, Logger);
  126. }
  127. /// <summary>
  128. /// Replaces the configuration.
  129. /// </summary>
  130. /// <param name="newConfiguration">The new configuration.</param>
  131. /// <exception cref="System.ArgumentNullException">newConfiguration</exception>
  132. public virtual void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration)
  133. {
  134. if (newConfiguration == null)
  135. {
  136. throw new ArgumentNullException("newConfiguration");
  137. }
  138. ValidateCachePath(newConfiguration);
  139. CommonConfiguration = newConfiguration;
  140. SaveConfiguration();
  141. }
  142. /// <summary>
  143. /// Updates the items by name path.
  144. /// </summary>
  145. private void UpdateCachePath()
  146. {
  147. string cachePath;
  148. if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath))
  149. {
  150. cachePath = null;
  151. }
  152. else if (CommonConfiguration.EnableCustomPathSubFolders)
  153. {
  154. cachePath = Path.Combine(CommonConfiguration.CachePath, "cache");
  155. }
  156. else
  157. {
  158. cachePath = CommonConfiguration.CachePath;
  159. }
  160. ((BaseApplicationPaths)CommonApplicationPaths).CachePath = cachePath;
  161. }
  162. /// <summary>
  163. /// Replaces the cache path.
  164. /// </summary>
  165. /// <param name="newConfig">The new configuration.</param>
  166. /// <exception cref="System.IO.DirectoryNotFoundException"></exception>
  167. private void ValidateCachePath(BaseApplicationConfiguration newConfig)
  168. {
  169. var newPath = newConfig.CachePath;
  170. if (!string.IsNullOrWhiteSpace(newPath)
  171. && !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath))
  172. {
  173. // Validate
  174. if (!Directory.Exists(newPath))
  175. {
  176. throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath));
  177. }
  178. EnsureWriteAccess(newPath);
  179. }
  180. }
  181. protected void EnsureWriteAccess(string path)
  182. {
  183. var file = Path.Combine(path, Guid.NewGuid().ToString());
  184. FileSystem.WriteAllText(file, string.Empty);
  185. FileSystem.DeleteFile(file);
  186. }
  187. private readonly ConcurrentDictionary<string, object> _configurations = new ConcurrentDictionary<string, object>();
  188. private string GetConfigurationFile(string key)
  189. {
  190. return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLower() + ".xml");
  191. }
  192. public object GetConfiguration(string key)
  193. {
  194. return _configurations.GetOrAdd(key, k =>
  195. {
  196. var file = GetConfigurationFile(key);
  197. var configurationType = _configurationStores
  198. .First(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase))
  199. .ConfigurationType;
  200. lock (_configurationSyncLock)
  201. {
  202. return LoadConfiguration(file, configurationType);
  203. }
  204. });
  205. }
  206. private object LoadConfiguration(string path, Type configurationType)
  207. {
  208. try
  209. {
  210. return XmlSerializer.DeserializeFromFile(configurationType, path);
  211. }
  212. catch (FileNotFoundException)
  213. {
  214. return Activator.CreateInstance(configurationType);
  215. }
  216. catch (DirectoryNotFoundException)
  217. {
  218. return Activator.CreateInstance(configurationType);
  219. }
  220. catch (Exception ex)
  221. {
  222. Logger.ErrorException("Error loading configuration file: {0}", ex, path);
  223. return Activator.CreateInstance(configurationType);
  224. }
  225. }
  226. public void SaveConfiguration(string key, object configuration)
  227. {
  228. var configurationStore = GetConfigurationStore(key);
  229. var configurationType = configurationStore.ConfigurationType;
  230. if (configuration.GetType() != configurationType)
  231. {
  232. throw new ArgumentException("Expected configuration type is " + configurationType.Name);
  233. }
  234. var validatingStore = configurationStore as IValidatingConfiguration;
  235. if (validatingStore != null)
  236. {
  237. var currentConfiguration = GetConfiguration(key);
  238. validatingStore.Validate(currentConfiguration, configuration);
  239. }
  240. EventHelper.FireEventIfNotNull(NamedConfigurationUpdating, this, new ConfigurationUpdateEventArgs
  241. {
  242. Key = key,
  243. NewConfiguration = configuration
  244. }, Logger);
  245. _configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
  246. var path = GetConfigurationFile(key);
  247. Directory.CreateDirectory(Path.GetDirectoryName(path));
  248. lock (_configurationSyncLock)
  249. {
  250. XmlSerializer.SerializeToFile(configuration, path);
  251. }
  252. OnNamedConfigurationUpdated(key, configuration);
  253. }
  254. protected virtual void OnNamedConfigurationUpdated(string key, object configuration)
  255. {
  256. EventHelper.FireEventIfNotNull(NamedConfigurationUpdated, this, new ConfigurationUpdateEventArgs
  257. {
  258. Key = key,
  259. NewConfiguration = configuration
  260. }, Logger);
  261. }
  262. public Type GetConfigurationType(string key)
  263. {
  264. return GetConfigurationStore(key)
  265. .ConfigurationType;
  266. }
  267. private ConfigurationStore GetConfigurationStore(string key)
  268. {
  269. return _configurationStores
  270. .First(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase));
  271. }
  272. }
  273. }