├── tests
└── ConfigManager.Tests
│ ├── Config
│ ├── TestYaml.yaml
│ ├── TestYaml2.yaml
│ ├── TestYaml3.yaml
│ ├── Test1.conf
│ ├── Test2.conf
│ ├── Test3.conf
│ └── TestJson.json
│ ├── packages.config
│ ├── ConfigClasses
│ └── TestConfig.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── ConfigManager.Tests.csproj
│ └── ConfigManagerTests.cs
├── NuGet
├── NuGetPack.cmd
└── ConfigManager
│ └── ConfigManager.nuspec
├── lib
├── ConfigManager.dll
├── ConfigManager.pdb
└── ConfigManager.XML
├── src
└── ConfigManager
│ ├── packages.config
│ ├── ConfigClasses
│ └── ConfigPathsConfig.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── ConfigManager.sln
│ ├── ConfigManager.csproj
│ └── ConfigManager.cs
├── LICENSE
├── .gitignore
└── README.md
/tests/ConfigManager.Tests/Config/TestYaml.yaml:
--------------------------------------------------------------------------------
1 | Foo: 1
2 | Bar: 2
3 | Baz: 3
--------------------------------------------------------------------------------
/tests/ConfigManager.Tests/Config/TestYaml2.yaml:
--------------------------------------------------------------------------------
1 | Foo: a
2 | Bar: b
3 | Baz: c
--------------------------------------------------------------------------------
/NuGet/NuGetPack.cmd:
--------------------------------------------------------------------------------
1 | nuget pack "ConfigManager\ConfigManager.nuspec" -symbols
2 |
--------------------------------------------------------------------------------
/tests/ConfigManager.Tests/Config/TestYaml3.yaml:
--------------------------------------------------------------------------------
1 | Foo: "!"
2 | Bar: "@"
3 | Baz: "#"
--------------------------------------------------------------------------------
/lib/ConfigManager.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tejacques/ConfigManager/HEAD/lib/ConfigManager.dll
--------------------------------------------------------------------------------
/lib/ConfigManager.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tejacques/ConfigManager/HEAD/lib/ConfigManager.pdb
--------------------------------------------------------------------------------
/tests/ConfigManager.Tests/Config/Test1.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Foo" : "1",
3 | "Bar" : "2",
4 | "Baz" : "3"
5 | }
--------------------------------------------------------------------------------
/tests/ConfigManager.Tests/Config/Test2.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Foo" : "a",
3 | "Bar" : "b",
4 | "Baz" : "c"
5 | }
--------------------------------------------------------------------------------
/tests/ConfigManager.Tests/Config/Test3.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Foo" : "!",
3 | "Bar" : "@",
4 | "Baz" : "#"
5 | }
--------------------------------------------------------------------------------
/tests/ConfigManager.Tests/Config/TestJson.json:
--------------------------------------------------------------------------------
1 | {
2 | "Foo" : "1",
3 | "Bar" : "2",
4 | "Baz" : "3"
5 | }
--------------------------------------------------------------------------------
/src/ConfigManager/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/tests/ConfigManager.Tests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/tests/ConfigManager.Tests/ConfigClasses/TestConfig.cs:
--------------------------------------------------------------------------------
1 | namespace ConfigManager.Tests.ConfigClasses
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 |
8 | public class TestConfig
9 | {
10 | public TestConfig()
11 | {
12 | Foo = "foo";
13 | Bar = "bar";
14 | Baz = "baz";
15 | }
16 | public string Foo { get; set; }
17 | public string Bar { get; set; }
18 | public string Baz { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2013 Tom Jacques
2 | https://github.com/tejacques/ConfigManager
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/ConfigManager/ConfigClasses/ConfigPathsConfig.cs:
--------------------------------------------------------------------------------
1 | namespace ConfigManager.ConfigClasses
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Configuration;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Text;
9 |
10 | ///
11 | /// The strongly typed class to deserialize the
12 | /// global configuration file ConfigPaths.conf
13 | ///
14 | public class ConfigPathsConfig
15 | {
16 | ///
17 | /// An ordered list of search paths to search
18 | /// in for configuration files.
19 | ///
20 | public DirectoryInfo[] Paths { get; set; }
21 |
22 | ///
23 | /// Contructs the configuration with the default
24 | /// values for search paths, which are: the local
25 | /// application root directory, C:/Configs, and
26 | /// D:/Configs
27 | ///
28 | public ConfigPathsConfig()
29 | {
30 | DirectoryInfo baseDirectory = new DirectoryInfo(
31 | AppDomain.CurrentDomain.BaseDirectory);
32 | while (baseDirectory.Name.ToLowerInvariant().EndsWith("bin")
33 | || baseDirectory.Name.ToLowerInvariant().EndsWith("debug")
34 | || baseDirectory.Name.ToLowerInvariant().EndsWith("release"))
35 | {
36 | baseDirectory = baseDirectory.Parent;
37 | }
38 | Paths = new DirectoryInfo[] { baseDirectory };
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/ConfigManager/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ConfigManager")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ConfigManager")]
13 | [assembly: AssemblyCopyright("Copyright © 2013")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("efdf8c44-9edf-425a-ae80-ba986c87b2a1")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/tests/ConfigManager.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ConfigManager.Tests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ConfigManager.Tests")]
13 | [assembly: AssemblyCopyright("Copyright © 2013")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("1b2abcce-4806-4c8a-888a-e9e9a328e448")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/src/ConfigManager/ConfigManager.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.30110.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigManager", "ConfigManager.csproj", "{6DFDDA59-1266-45D7-8B54-332D7B686ACE}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigManager.Tests", "..\..\tests\ConfigManager.Tests\ConfigManager.Tests.csproj", "{61F2083F-81E0-44CF-A529-D65B74E6CC2B}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {6DFDDA59-1266-45D7-8B54-332D7B686ACE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {6DFDDA59-1266-45D7-8B54-332D7B686ACE}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {6DFDDA59-1266-45D7-8B54-332D7B686ACE}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {6DFDDA59-1266-45D7-8B54-332D7B686ACE}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {61F2083F-81E0-44CF-A529-D65B74E6CC2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {61F2083F-81E0-44CF-A529-D65B74E6CC2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {61F2083F-81E0-44CF-A529-D65B74E6CC2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {61F2083F-81E0-44CF-A529-D65B74E6CC2B}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
2 | [Bb]in/
3 | [Oo]bj/
4 |
5 | # mstest test results
6 | TestResults
7 |
8 | ## Ignore Visual Studio temporary files, build results, and
9 | ## files generated by popular Visual Studio add-ons.
10 |
11 | # User-specific files
12 | *.suo
13 | *.user
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Rr]elease/
19 | x64/
20 | *_i.c
21 | *_p.c
22 | *.ilk
23 | *.meta
24 | *.obj
25 | *.pch
26 | *.pgc
27 | *.pgd
28 | *.rsp
29 | *.sbr
30 | *.tlb
31 | *.tli
32 | *.tlh
33 | *.tmp
34 | *.log
35 | *.vspscc
36 | *.vssscc
37 | .builds
38 |
39 | # Visual C++ cache files
40 | ipch/
41 | *.aps
42 | *.ncb
43 | *.opensdf
44 | *.sdf
45 |
46 | # Visual Studio profiler
47 | *.psess
48 | *.vsp
49 | *.vspx
50 |
51 | # Guidance Automation Toolkit
52 | *.gpState
53 |
54 | # ReSharper is a .NET coding add-in
55 | _ReSharper*
56 |
57 | # NCrunch
58 | *.ncrunch*
59 | .*crunch*.local.xml
60 |
61 | # Installshield output folder
62 | [Ee]xpress
63 |
64 | # DocProject is a documentation generator add-in
65 | DocProject/buildhelp/
66 | DocProject/Help/*.HxT
67 | DocProject/Help/*.HxC
68 | DocProject/Help/*.hhc
69 | DocProject/Help/*.hhk
70 | DocProject/Help/*.hhp
71 | DocProject/Help/Html2
72 | DocProject/Help/html
73 |
74 | # Click-Once directory
75 | publish
76 |
77 | # Publish Web Output
78 | *.Publish.xml
79 |
80 | # NuGet Packages Directory
81 | packages
82 |
83 | # Windows Azure Build Output
84 | csx
85 | *.build.csdef
86 |
87 | # Windows Store app package directory
88 | AppPackages/
89 |
90 | # Others
91 | [Bb]in
92 | [Oo]bj
93 | sql
94 | TestResults
95 | [Tt]est[Rr]esult*
96 | *.Cache
97 | ClientBin
98 | [Ss]tyle[Cc]op.*
99 | ~$*
100 | *.dbmdl
101 | Generated_Code #added for RIA/Silverlight projects
102 |
103 | # Backup & report files from converting an old project file to a newer
104 | # Visual Studio version. Backup files are not needed, because we have git ;-)
105 | _UpgradeReport_Files/
106 | Backup*/
107 | UpgradeLog*.XML
108 |
--------------------------------------------------------------------------------
/NuGet/ConfigManager/ConfigManager.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ConfigManager
5 | 2.0.1
6 | ConfigManager
7 | Tom Jacques
8 | Tom Jacques
9 | https://github.com/tejacques/ConfigManager/blob/master/LICENSE
10 | https://github.com/tejacques/ConfigManager
11 | false
12 |
13 | Binaries for the ConfigManager library.
14 | Visit https://github.com/tejacques/ConfigManager for an overview and usage examples.
15 |
16 |
17 | An open source helper library for retrieving strongly typed automatically updating configuration data from config files.
18 |
19 |
20 | Notes:
21 |
22 | Version 2.0.1:
23 | - Fix bug where LogException was called without a null check.
24 |
25 | Version 2.0.0:
26 | - Remove Parsed object from Configuration class
27 | - GetConfig now returns a copy of the configuration so that the Configuration held by the manager is immutable.
28 | - Remove NLog Dependency, instead user can provide Log and LogException functions to ConfigManager.
29 |
30 | Version 1.1.1:
31 | - Include source files and pdbs.
32 |
33 | Version 1.1.0:
34 | - Add a delegate to retrieve the configuration from a user defined location (ex: read config from a database).
35 | - Add a delegate to run if the configuration file is newer than the configuration from the user defined location (ex: update database).
36 | - Do not log an error for a missing configuration file, only if the file existed and failed to read.
37 |
38 | Version 1.0.3:
39 | - Update dependency version compatibility
40 |
41 | Version 1.0.2:
42 | - Add a DevMode setting. If true, ConfigManager will first check for config files ending with .dev.conf.
43 | Useful for having different settings on development.
44 |
45 | Version 1.0.1:
46 | - Fix a bug in updating files where the key had an incorrect substring check
47 |
48 | Version 1.0.0:
49 | - File handlers to automatically update config data in the running program
50 | - Generic functions to retrieve configuration data as a strongly typed object
51 |
52 |
53 |
54 |
55 | Copyright 2014
56 | Config Configuration auto updating
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ConfigManager
2 | =============
3 |
4 | What is it?
5 | -----------
6 |
7 | A json configuration file manager for C# / .NET / Mono
8 |
9 | How can I get it?
10 | -----------------
11 |
12 | ConfigManager is available as a NuGet package: https://www.nuget.org/packages/ConfigManager/
13 |
14 | ```
15 | PM> Install-Package ConfigManager
16 | ```
17 |
18 | Why was it made?
19 | ----------------
20 |
21 | I wanted a strongly typed configuration system that would automatically update it's values in long running processes, so if the configuration file changed, the changes would propagate automatically without restarting the binary or interupting service.
22 |
23 | Example Usage
24 | -------------
25 |
26 | ConfigManager is a globally available static class that is typically used as follows:
27 |
28 | ProjectDirectory/Config/configuredNames.conf
29 | ```json
30 | [
31 | "Billy",
32 | "John",
33 | "Harold"
34 | ]
35 | ```
36 |
37 | ```csharp
38 | public void Example()
39 | {
40 | List configuredNames = ConfigManager.GetCreateConfig>("configuredNames");
41 | foreach(string name in configuredNames)
42 | {
43 | Console.WriteLine(name);
44 | }
45 | }
46 | ```
47 |
48 | The output will be:
49 | ```
50 | Billy
51 | John
52 | Harold
53 | ```
54 |
55 | ### Config File Types ###
56 |
57 | Config Manager supports both json and yaml. The type is differentiated by the file extension. A .conf or .json file is deserialized as Json, a .yaml file is deserialized as Yaml.
58 |
59 | ### Creating a Configuration class to use
60 |
61 | Part of the magic is that ConfigManager can translate your config files to any strongly typed object you define. The only constraint is that it must implement new(). The reason why is so that everything that comes back from the command is guaranteed to not be null.
62 |
63 | ```csharp
64 | public class ExampleConfig
65 | {
66 | public string String { get; set; }
67 | public int Int { get; set; }
68 | public Website { get; set; }
69 |
70 | // Provide the default values of your config class here
71 | public ExampleConfig()
72 | {
73 | String = "Default String";
74 | Int = 0;
75 | Website = "www.example.org";
76 | }
77 | }
78 | ```
79 |
80 | ProjectDirectory/Config/ExampleConfig.conf
81 |
82 | ```json
83 | {
84 | "String" : "Test",
85 | "Int" : 10,
86 | "Website" : "https://github.com/tejacques/ConfigManager/"
87 | }
88 | ```
89 |
90 | ```csharp
91 | public void ExampleConfigTest()
92 | {
93 | ExampleConfig exampleConfig = ConfigManager.GetCreateConfig("ExampleConfig");
94 | Console.WriteLine(exampleConfig.String); // Test
95 | Console.WriteLine(exampleConfig.Int); // 10
96 | Console.WriteLine(exampleConfig.Website); // https://github.com/tejacques/ConfigManager/
97 | }
98 | ```
99 |
100 | Additional Features
101 | -------------------
102 |
103 | While in debug mode, ConfigManager will first look for files that end in `.dev.(conf|json|yaml)` with the given name, unless a fully specified file path is given. This is ideal for things that differ between dev and prod such as a connection string.
104 |
--------------------------------------------------------------------------------
/src/ConfigManager/ConfigManager.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {6DFDDA59-1266-45D7-8B54-332D7B686ACE}
8 | Library
9 | Properties
10 | ConfigManager
11 | ConfigManager
12 | v4.5
13 | 512
14 | ..\..\..\..\src\
15 | true
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 | bin\Release\ConfigManager.XML
34 |
35 |
36 |
37 | False
38 | packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | packages\YamlDotNet.3.1.1\lib\net35\YamlDotNet.dll
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
68 |
--------------------------------------------------------------------------------
/tests/ConfigManager.Tests/ConfigManager.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {61F2083F-81E0-44CF-A529-D65B74E6CC2B}
8 | Library
9 | Properties
10 | ConfigManager.Tests
11 | ConfigManager.Tests
12 | v4.5
13 | 512
14 | ..\
15 | true
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 | False
37 | ..\..\src\ConfigManager\packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll
38 |
39 |
40 | ..\..\src\ConfigManager\packages\NUnit.2.6.3\lib\nunit.framework.dll
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | False
52 | ..\..\src\ConfigManager\packages\YamlDotNet.3.1.1\lib\net35\YamlDotNet.dll
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | {6dfdda59-1266-45d7-8b54-332d7b686ace}
73 | ConfigManager
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
88 |
--------------------------------------------------------------------------------
/lib/ConfigManager.XML:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ConfigManager
5 |
6 |
7 |
8 |
9 | A data type containing metadata about a configuration item.
10 |
11 |
12 |
13 |
14 | The name of the configuration item.
15 |
16 |
17 |
18 |
19 | The raw configuration data.
20 |
21 |
22 |
23 |
24 | The time the configuration file was last updated.
25 |
26 |
27 |
28 |
29 | A bool indicating whether the configuration item came from
30 | ConfigManager or a user-specified source delegate.
31 |
32 |
33 |
34 |
35 | A data type containing metadata about a configuration file.
36 |
37 |
38 |
39 |
40 | The raw text read from the configuration file.
41 |
42 |
43 |
44 |
45 | The filepath to the configuration file.
46 |
47 |
48 |
49 |
50 | The time the configuration file was last updated.
51 |
52 |
53 |
54 |
55 | A utility class to make managing configuration files painless.
56 | ConfigManager uses json configuration files which map to strongly
57 | typed C# objects using ServiceStack.Text.DeserializeJson
58 |
59 |
60 |
61 |
62 | Adds a configuration to the manager.
63 |
64 |
65 | The type of the configuration object.
66 |
67 |
68 | The name of the configuration object to add.
69 |
70 |
71 | The file path (relative or absolute) to add.
72 |
73 |
74 | Optional boolean argument that specifies
75 | the config should be updated if it exists.
76 |
77 |
78 |
79 |
80 | Adds and then gets the configuration of a given name.
81 |
82 |
83 | The type of the configuration object.
84 |
85 |
86 | The name of the configuration object to add.
87 |
88 |
89 | The file path (relative or absolute) to add.
90 |
91 |
92 | Optional boolean argument that specifies the
93 | config should be updated if it exists.
94 |
95 |
96 | An object of type T with the values in the specified config file.
97 |
98 |
99 |
100 |
101 | Creates a Configuration of the given type
102 | from the specified file path.
103 |
104 |
105 | The type of the configuration object.
106 |
107 |
108 | The name of the configuration.
109 |
110 |
111 | The file path (relative or absolute) of the configuration file.
112 |
113 |
114 | An object of type T with the values in the specified config file.
115 |
116 |
117 |
118 |
119 | Gets a configuration item from a file.
120 |
121 | The name of the configuration.
122 | The path of the file.
123 |
124 | The configuration item pulled from the specified file.
125 |
126 |
127 |
128 |
129 | Get a configuration item from a delegate.
130 |
131 | The name of the configuration.
132 | The configuration item pulled from the delegate.
133 |
134 |
135 |
136 | Remove the specified config file from the ConfigManager
137 |
138 | The specified config file key
139 |
140 |
141 |
142 | Returns the configuration object of the specified type and name
143 | from the in-memory dictionary of the ConfigManager.
144 |
145 | The type of the configuration object.
146 |
147 | The name of the configuration
148 | object to retrieve.
149 | An object of type T with the values in the
150 | ConfigManager.
151 |
152 |
153 |
154 | Returns the DateTime that the specified file was last written to.
155 |
156 | The file path.
157 |
158 | The DateTime that the specified file was last written to.
159 |
160 |
161 |
162 |
163 | Reads a relative or absolute file path and returns the contents.
164 | Nonrooted file paths are checked relative to the configured
165 | search directories.
166 |
167 | The file path.
168 | The contents of the file that was found, or empty string
169 | if no file present.
170 |
171 |
172 |
173 | Parses json into the specified object type.
174 |
175 | The object type to return.
176 | The raw json.
177 |
178 | The contents of the raw json as the specified object type.
179 |
180 |
181 |
182 |
183 | Development mode. If true, ConfigManager will look for
184 | files ending with .dev.conf rather than .conf
185 |
186 |
187 |
188 |
189 | User provided Log function.
190 |
191 |
192 |
193 |
194 | User provided LogException function
195 |
196 |
197 |
198 |
199 | A delegate settable by the user which says how to try
200 | and get the configuration before going to the file system.
201 |
202 |
203 |
204 |
205 | A delegate settable by the user which says what to do after
206 | getting the configuration from the file system.
207 |
208 |
209 |
210 |
211 | A Utility Enum to ensure that typos aren't make for
212 | global configuration files.
213 |
214 |
215 |
216 |
217 | Maps to the string of the same name.
218 |
219 |
220 |
221 |
222 | The strongly typed class to deserialize the
223 | global configuration file ConfigPaths.conf
224 |
225 |
226 |
227 |
228 | Contructs the configuration with the default
229 | values for search paths, which are: the local
230 | application root directory, C:/Configs, and
231 | D:/Configs
232 |
233 |
234 |
235 |
236 | An ordered list of search paths to search
237 | in for configuration files.
238 |
239 |
240 |
241 |
242 |
--------------------------------------------------------------------------------
/tests/ConfigManager.Tests/ConfigManagerTests.cs:
--------------------------------------------------------------------------------
1 | namespace ConfigManager.Tests
2 | {
3 | using ConfigClasses;
4 | using Newtonsoft.Json;
5 | using Newtonsoft.Json.Linq;
6 | using NUnit.Framework;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.IO;
10 | using System.Linq;
11 | using System.Text;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | [TestFixture]
16 | public class ConfigManagerTests
17 | {
18 | private static int loops;
19 | private static string json = @"
20 | [
21 | ""Foo"",
22 | ""Bar"",
23 | ""Baz"",
24 | ""1"",""2"",""3"",""4"",""5"",""6"",""7"",""8"",""9"",""10"",
25 | ""1"",""2"",""3"",""4"",""5"",""6"",""7"",""8"",""9"",""10"",
26 | ""1"",""2"",""3"",""4"",""5"",""6"",""7"",""8"",""9"",""10"",
27 | ""1"",""2"",""3"",""4"",""5"",""6"",""7"",""8"",""9"",""10"",
28 | ""1"",""2"",""3"",""4"",""5"",""6"",""7"",""8"",""9"",""10""
29 | ]
30 | ";
31 | [TestFixtureSetUp]
32 | public void SetUp()
33 | {
34 | loops = 100000;
35 | ConfigManager.DevMode = true;
36 | var parsedJsonDotNet = JsonConvert.DeserializeObject>(json);
37 | }
38 |
39 | [Test]
40 | public static void TestConfigManager()
41 | {
42 | TestConfig config =
43 | ConfigManager.GetCreateConfig("TestDefault");
44 | Assert.NotNull(config);
45 | }
46 |
47 | [Test]
48 | public static void TestConfigParse()
49 | {
50 | TestConfig config = ConfigManager.GetCreateConfig
51 | ("Test1");
52 | Assert.NotNull(config);
53 | Assert.AreEqual("1", config.Foo);
54 | Assert.AreEqual("2", config.Bar);
55 | Assert.AreEqual("3", config.Baz);
56 | }
57 |
58 | [Test]
59 | public static void TestConfigParseJson()
60 | {
61 | TestConfig config = ConfigManager.GetCreateConfig
62 | ("TestJson");
63 | Assert.NotNull(config);
64 | Assert.AreEqual("1", config.Foo);
65 | Assert.AreEqual("2", config.Bar);
66 | Assert.AreEqual("3", config.Baz);
67 | }
68 |
69 | [Test]
70 | public static void TestConfigParseYaml()
71 | {
72 | TestConfig config = ConfigManager.GetCreateConfig
73 | ("TestYaml");
74 | Assert.NotNull(config);
75 | Assert.AreEqual("1", config.Foo);
76 | Assert.AreEqual("2", config.Bar);
77 | Assert.AreEqual("3", config.Baz);
78 | }
79 |
80 | [Test]
81 | public static void TestConfigParseSerial()
82 | {
83 | for (int i = 0; i < loops; i++)
84 | {
85 | TestConfig config = ConfigManager.GetCreateConfig
86 | ("Test2");
87 | Assert.NotNull(config);
88 | Assert.AreEqual("a", config.Foo);
89 | Assert.AreEqual("b", config.Bar);
90 | Assert.AreEqual("c", config.Baz);
91 | }
92 | }
93 |
94 | [Test]
95 | public static void TestConfigParseSerialYaml()
96 | {
97 | for (int i = 0; i < loops; i++)
98 | {
99 | TestConfig config = ConfigManager.GetCreateConfig
100 | ("TestYaml2");
101 | Assert.NotNull(config);
102 | Assert.AreEqual("a", config.Foo);
103 | Assert.AreEqual("b", config.Bar);
104 | Assert.AreEqual("c", config.Baz);
105 | }
106 | }
107 |
108 | [Test]
109 | public static void TestConfigParseParallel()
110 | {
111 | Parallel.For(0, loops, (x) =>
112 | {
113 | TestConfig config = ConfigManager.GetCreateConfig
114 | ("Test3");
115 | Assert.NotNull(config);
116 | Assert.AreEqual("!", config.Foo);
117 | Assert.AreEqual("@", config.Bar);
118 | Assert.AreEqual("#", config.Baz);
119 | });
120 | }
121 |
122 | [Test]
123 | public static void TestConfigParseParallelYaml()
124 | {
125 | Parallel.For(0, loops, (x) =>
126 | {
127 | TestConfig config = ConfigManager.GetCreateConfig
128 | ("TestYaml3");
129 | Assert.NotNull(config);
130 | Assert.AreEqual("!", config.Foo);
131 | Assert.AreEqual("@", config.Bar);
132 | Assert.AreEqual("#", config.Baz);
133 | });
134 | }
135 |
136 | [Test]
137 | public static void TestConfigParseMultipleParallel()
138 | {
139 | Parallel.For(0, loops, (x) =>
140 | {
141 | TestConfig config = ConfigManager.GetCreateConfig
142 | ("Test_" + (x % 100), "");
143 | Assert.NotNull(config);
144 | Assert.AreEqual("foo", config.Foo);
145 | Assert.AreEqual("bar", config.Bar);
146 | Assert.AreEqual("baz", config.Baz);
147 | });
148 | }
149 |
150 | [Test]
151 | public static void TestConfigEvents()
152 | {
153 | // Test getting the config when the config file doesn't exist
154 | TestConfig config =
155 | ConfigManager.GetCreateConfig("TestUpdate");
156 | Assert.NotNull(config);
157 | Assert.AreEqual("foo", config.Foo);
158 | Assert.AreEqual("bar", config.Bar);
159 | Assert.AreEqual("baz", config.Baz);
160 |
161 | // Create a new config file based on Test1
162 | string path = "TestUpdate.conf";
163 | using (StreamWriter sw = new StreamWriter(path, append:true))
164 | {
165 | sw.Write(JsonConvert.SerializeObject(
166 | ConfigManager
167 | .GetCreateConfig("Test1")));
168 | sw.Flush();
169 | }
170 |
171 | // Sleep to let the ConfigManager deal with the events
172 | Thread.Sleep(100);
173 |
174 | // Re-grab the config from the ConfigManager
175 | config = ConfigManager.GetCreateConfig("TestUpdate");
176 | Assert.NotNull(config);
177 | Assert.AreEqual("1", config.Foo);
178 | Assert.AreEqual("2", config.Bar);
179 | Assert.AreEqual("3", config.Baz);
180 |
181 | using (StreamWriter sw = new StreamWriter(path, false))
182 | {
183 | sw.Write(JsonConvert.SerializeObject(
184 | ConfigManager
185 | .GetCreateConfig("Test2")));
186 | sw.Flush();
187 | }
188 |
189 | // Sleep to let the ConfigManager deal with the events
190 | Thread.Sleep(100);
191 |
192 | // Re-grab the config from the ConfigManager
193 | config = ConfigManager.GetCreateConfig("TestUpdate");
194 | Assert.NotNull(config);
195 | Assert.AreEqual("a", config.Foo);
196 | Assert.AreEqual("b", config.Bar);
197 | Assert.AreEqual("c", config.Baz);
198 |
199 | // Delete the config file
200 | if (File.Exists(path))
201 | {
202 | File.Delete(path);
203 | }
204 |
205 | // Sleep to let the ConfigManager deal with the events
206 | Thread.Sleep(100);
207 |
208 | // Re-grab the config from the ConfigManager
209 | config = ConfigManager.GetCreateConfig("TestUpdate");
210 | Assert.NotNull(config);
211 | Assert.AreEqual("foo", config.Foo);
212 | Assert.AreEqual("bar", config.Bar);
213 | Assert.AreEqual("baz", config.Baz);
214 | }
215 |
216 | [Test]
217 | public static void TestConfigEventsYaml()
218 | {
219 | // Test getting the config when the config file doesn't exist
220 | TestConfig config =
221 | ConfigManager.GetCreateConfig("TestUpdateYaml");
222 | Assert.NotNull(config);
223 | Assert.AreEqual("foo", config.Foo);
224 | Assert.AreEqual("bar", config.Bar);
225 | Assert.AreEqual("baz", config.Baz);
226 |
227 | // Create a new config file based on Test1
228 | string path = "TestUpdateYaml.yaml";
229 | using (StreamWriter sw = new StreamWriter(path, append: true))
230 | {
231 | var serializer = new YamlDotNet.Serialization.Serializer();
232 | serializer.Serialize(sw,
233 | ConfigManager
234 | .GetCreateConfig("Test1"));
235 |
236 | sw.Flush();
237 | }
238 |
239 | // Sleep to let the ConfigManager deal with the events
240 | Thread.Sleep(100);
241 |
242 | // Re-grab the config from the ConfigManager
243 | config = ConfigManager.GetCreateConfig("TestUpdateYaml");
244 | Assert.NotNull(config);
245 | Assert.AreEqual("1", config.Foo);
246 | Assert.AreEqual("2", config.Bar);
247 | Assert.AreEqual("3", config.Baz);
248 |
249 | using (StreamWriter sw = new StreamWriter(path, append: false))
250 | {
251 | var serializer = new YamlDotNet.Serialization.Serializer();
252 | serializer.Serialize(sw,
253 | ConfigManager
254 | .GetCreateConfig("Test2"));
255 |
256 | sw.Flush();
257 | }
258 |
259 | // Sleep to let the ConfigManager deal with the events
260 | Thread.Sleep(100);
261 |
262 | // Re-grab the config from the ConfigManager
263 | config = ConfigManager.GetCreateConfig("TestUpdateYaml");
264 | Assert.NotNull(config);
265 | Assert.AreEqual("a", config.Foo);
266 | Assert.AreEqual("b", config.Bar);
267 | Assert.AreEqual("c", config.Baz);
268 |
269 | // Delete the config file
270 | if (File.Exists(path))
271 | {
272 | File.Delete(path);
273 | }
274 |
275 | // Sleep to let the ConfigManager deal with the events
276 | Thread.Sleep(100);
277 |
278 | // Re-grab the config from the ConfigManager
279 | config = ConfigManager.GetCreateConfig("TestUpdateYaml");
280 | Assert.NotNull(config);
281 | Assert.AreEqual("foo", config.Foo);
282 | Assert.AreEqual("bar", config.Bar);
283 | Assert.AreEqual("baz", config.Baz);
284 | }
285 |
286 | [TestFixtureTearDown]
287 | public static void TearDown()
288 | {
289 | // Delete the config file in case there was an error
290 | string[] paths = { "TestUpdate.conf", "TestUpdateYaml.yaml" };
291 |
292 | foreach (string path in paths)
293 | {
294 | if (File.Exists(path))
295 | {
296 | File.Delete(path);
297 | }
298 | }
299 | }
300 | }
301 | }
302 |
--------------------------------------------------------------------------------
/src/ConfigManager/ConfigManager.cs:
--------------------------------------------------------------------------------
1 | namespace ConfigManager
2 | {
3 | using ConfigClasses;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Collections.Concurrent;
7 | using System.Diagnostics;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Timers;
12 | using System.Threading.Tasks;
13 |
14 | ///
15 | /// The type of configuration file
16 | ///
17 | public enum ConfigType
18 | {
19 | ///
20 | /// The config file type is unknown
21 | ///
22 | Unknown,
23 | ///
24 | /// The config file is Json
25 | ///
26 | Json,
27 | ///
28 | /// The config file is Yaml
29 | ///
30 | Yaml
31 | }
32 |
33 | ///
34 | /// A data type containing metadata about a configuration item.
35 | ///
36 | public class ConfigurationItem
37 | {
38 | ///
39 | /// The name of the configuration item.
40 | ///
41 | public string Name { get; set; }
42 |
43 | ///
44 | /// The raw configuration data.
45 | ///
46 | public string Data { get; set; }
47 |
48 | ///
49 | /// The time the configuration file was last updated.
50 | ///
51 | public DateTime LastUpdated { get; set; }
52 |
53 | ///
54 | /// A bool indicating whether the configuration item came from
55 | /// ConfigManager or a user-specified source delegate.
56 | ///
57 | public bool FromConfigManager { get; set; }
58 | }
59 |
60 | ///
61 | /// A data type containing metadata about a configuration file.
62 | ///
63 | public class Configuration
64 | {
65 | ///
66 | /// The raw text read from the configuration file.
67 | ///
68 | public string Raw { get; set; }
69 | ///
70 | /// The parsed object
71 | ///
72 | public object Parsed { get; set; }
73 | ///
74 | /// The filepath to the configuration file.
75 | ///
76 | public string FilePath { get; set; }
77 | ///
78 | /// The time the configuration file was last updated.
79 | ///
80 | public DateTime LastUpdated { get; set; }
81 | ///
82 | /// The type of config file.
83 | ///
84 | public ConfigType ConfigType { get; set; }
85 | }
86 |
87 | ///
88 | /// A utility class to make managing configuration files painless.
89 | /// ConfigManager uses json configuration files which map to strongly
90 | /// typed C# objects using ServiceStack.Text.DeserializeJson
91 | ///
92 | public class ConfigManager
93 | {
94 | private static ConcurrentDictionary _configs;
95 | private static List _fsWatchers;
96 | private static readonly object _fsWatcherLock = new object();
97 | private static readonly YamlDotNet
98 | .Serialization
99 | .Deserializer _yamlDeserializer =
100 | new YamlDotNet
101 | .Serialization
102 | .Deserializer(null, null, true);
103 |
104 | private static Func _getConfiguration;
105 | private static Action _putConfiguration;
106 |
107 | ///
108 | /// Development mode. If true, ConfigManager will look for
109 | /// files ending with .dev.conf rather than .conf
110 | ///
111 | public static bool DevMode { get; set; }
112 |
113 | ///
114 | /// User provided Log function.
115 | ///
116 | public static Action Log{ get; set; }
117 |
118 | ///
119 | /// User provided LogException function
120 | ///
121 | public static Action LogException { get; set; }
122 |
123 | ///
124 | /// A Utility Enum to ensure that typos aren't make for
125 | /// global configuration files.
126 | ///
127 | public enum ConfigKeys
128 | {
129 | ///
130 | /// Maps to the string of the same name.
131 | ///
132 | ConfigPathsConfig
133 | }
134 |
135 | static ConfigManager()
136 | {
137 | _configs = new ConcurrentDictionary();
138 |
139 | // TJ: Add the ConfigManagerConfig file default
140 | // to the class, then attempt to load
141 | AddConfig(
142 | ConfigKeys.ConfigPathsConfig.ToString());
143 | AddConfig(
144 | ConfigKeys.ConfigPathsConfig.ToString(), update: true);
145 |
146 | SetupWatchDirectories();
147 | }
148 |
149 | ///
150 | /// A delegate settable by the user which says how to try
151 | /// and get the configuration before going to the file system.
152 | ///
153 | public static Func GetConfiguration
154 | {
155 | private get { return _getConfiguration; }
156 | set { _getConfiguration = value; }
157 | }
158 |
159 | ///
160 | /// A delegate settable by the user which says what to do after
161 | /// getting the configuration from the file system.
162 | ///
163 | public static Action PutConfiguration
164 | {
165 | private get { return _putConfiguration; }
166 | set { _putConfiguration = value; }
167 | }
168 |
169 | ///
170 | /// Adds a configuration to the manager.
171 | ///
172 | ///
173 | /// The type of the configuration object.
174 | ///
175 | ///
176 | /// The name of the configuration object to add.
177 | ///
178 | ///
179 | /// The file path (relative or absolute) to add.
180 | ///
181 | ///
182 | /// Optional boolean argument that specifies
183 | /// the config should be updated if it exists.
184 | ///
185 | public static void AddConfig(
186 | string configName,
187 | string configPath = null,
188 | bool update = false)
189 | where T : new()
190 | {
191 | if (!update && _configs.ContainsKey(configName))
192 | {
193 | return;
194 | }
195 |
196 | if (configPath == null)
197 | {
198 | var tmpPath = configName + ".conf";
199 | configPath = tmpPath;
200 |
201 | if (FileExists(tmpPath))
202 | {
203 | configPath = tmpPath;
204 | }
205 | else if (FileExists(tmpPath = configName + ".json"))
206 | {
207 | configPath = tmpPath;
208 | }
209 | else if (FileExists(tmpPath = configName + ".yaml"))
210 | {
211 | configPath = tmpPath;
212 | }
213 | }
214 |
215 | _configs.AddOrUpdate(configName,
216 | (x) =>
217 | {
218 | return CreateConfig(configName, configPath);
219 | },
220 | (x, y) =>
221 | {
222 | if (update)
223 | {
224 | y = CreateConfig(configName, configPath);
225 | }
226 | return y;
227 | });
228 | }
229 |
230 | ///
231 | /// Adds and then gets the configuration of a given name.
232 | ///
233 | ///
234 | /// The type of the configuration object.
235 | ///
236 | ///
237 | /// The name of the configuration object to add.
238 | ///
239 | ///
240 | /// The file path (relative or absolute) to add.
241 | ///
242 | ///
243 | /// Optional boolean argument that specifies the
244 | /// config should be updated if it exists.
245 | ///
246 | ///
247 | /// An object of type T with the values in the specified config file.
248 | ///
249 | public static T GetCreateConfig(
250 | string configName,
251 | string configPath = null,
252 | bool update = false,
253 | bool cached = true)
254 | where T : new()
255 | {
256 | AddConfig(configName, configPath: configPath, update: update);
257 | return GetConfig(configName, cached);
258 | }
259 |
260 | ///
261 | /// Creates a Configuration of the given type
262 | /// from the specified file path.
263 | ///
264 | ///
265 | /// The type of the configuration object.
266 | ///
267 | ///
268 | /// The name of the configuration.
269 | ///
270 | ///
271 | /// The file path (relative or absolute) of the configuration file.
272 | ///
273 | ///
274 | /// An object of type T with the values in the specified config file.
275 | ///
276 | private static Configuration CreateConfig(
277 | string configName, string configPath)
278 | where T : new()
279 | {
280 | Configuration config = new Configuration();
281 |
282 | var path = GetPath(configPath);
283 | var fromFile = GetConfigFromFile(configName, path);
284 | var fromDelegate = GetConfigFromDelegate(configName);
285 | HandlePutConfig(fromFile, fromDelegate);
286 |
287 | var newest = fromFile.LastUpdated > fromDelegate.LastUpdated
288 | ? fromFile
289 | : fromDelegate;
290 |
291 | config.FilePath = path;
292 | config.LastUpdated = newest.LastUpdated;
293 | config.Raw = newest.Data;
294 |
295 | if (config.FilePath.Length >= 5)
296 | {
297 | var extension = config
298 | .FilePath
299 | .Substring(config.FilePath.Length - 5, 5)
300 | .ToLowerInvariant();
301 |
302 | switch(extension)
303 | {
304 | case ".conf":
305 | case ".json":
306 | config.ConfigType = ConfigType.Json;
307 | break;
308 | case ".yaml":
309 | config.ConfigType = ConfigType.Yaml;
310 | break;
311 | default:
312 | config.ConfigType = ConfigType.Unknown;
313 | break;
314 | }
315 | }
316 |
317 | config.Parsed = ParseConfig(config.Raw, config.ConfigType);
318 |
319 | return config;
320 | }
321 |
322 | ///
323 | /// Gets a configuration item from a file.
324 | ///
325 | /// The name of the configuration.
326 | /// The path of the file.
327 | ///
328 | /// The configuration item pulled from the specified file.
329 | ///
330 | private static ConfigurationItem GetConfigFromFile(
331 | string name, string path)
332 | {
333 | ConfigurationItem configItem = new ConfigurationItem();
334 |
335 | configItem.Name = name;
336 | configItem.Data = ReadConfig(path);
337 | configItem.LastUpdated = GetConfigUpdateTime(path);
338 | configItem.FromConfigManager = true;
339 |
340 | return configItem;
341 | }
342 |
343 | ///
344 | /// Get a configuration item from a delegate.
345 | ///
346 | /// The name of the configuration.
347 | /// The configuration item pulled from the delegate.
348 | private static ConfigurationItem GetConfigFromDelegate(string name)
349 | {
350 | ConfigurationItem configItem = null;
351 |
352 | if(null != _getConfiguration)
353 | {
354 | configItem = _getConfiguration(name);
355 | }
356 |
357 | if (null == configItem)
358 | {
359 | configItem = new ConfigurationItem();
360 | }
361 |
362 | configItem.Name = name;
363 |
364 | return configItem;
365 | }
366 |
367 | private static void HandlePutConfig(
368 | ConfigurationItem fromFile,
369 | ConfigurationItem fromDelegate)
370 | {
371 | if (null != _putConfiguration
372 | && fromFile.Name == fromDelegate.Name
373 | && fromFile.LastUpdated > fromDelegate.LastUpdated
374 | && !string.IsNullOrEmpty(fromFile.Data))
375 | {
376 | Task.Run(() => _putConfiguration(fromFile));
377 | }
378 | }
379 |
380 | private static string GetPath(string configPath)
381 | {
382 | string devConfigPath = "";
383 | if (DevMode)
384 | {
385 | devConfigPath = configPath
386 | .Replace(".conf", ".dev.conf")
387 | .Replace(".json", ".dev.json")
388 | .Replace(".yaml", ".dev.yaml");
389 | }
390 |
391 | if (!Path.IsPathRooted(configPath))
392 | {
393 | DirectoryInfo[] paths = GetConfig(
394 | ConfigKeys.ConfigPathsConfig.ToString()).Paths;
395 | foreach (DirectoryInfo path in paths)
396 | {
397 | // TJ: Skip over nonexistant paths
398 | if (path == null || !path.Exists)
399 | {
400 | continue;
401 | }
402 |
403 | FileInfo fi = null;
404 |
405 | if (DevMode)
406 | {
407 | fi = path.EnumerateFiles(
408 | devConfigPath,
409 | SearchOption.AllDirectories).FirstOrDefault();
410 | }
411 |
412 | if (null == fi)
413 | {
414 | fi = path.EnumerateFiles(
415 | configPath,
416 | SearchOption.AllDirectories).FirstOrDefault();
417 | }
418 | if (fi != null)
419 | {
420 | configPath = fi.FullName;
421 | break;
422 | }
423 | }
424 | }
425 |
426 | return configPath;
427 | }
428 |
429 | private static void SetupWatchDirectories()
430 | {
431 | lock (_fsWatcherLock)
432 | {
433 | _fsWatchers = new List();
434 |
435 | DirectoryInfo[] paths = GetConfig(
436 | ConfigKeys.ConfigPathsConfig.ToString()).Paths;
437 |
438 | var fsEventHandler =
439 | new FileSystemEventHandler(HandleUpdate);
440 | var errorEventHandler =
441 | new ErrorEventHandler(HandleError);
442 | var renamedEventHandler =
443 | new RenamedEventHandler(HandleRename);
444 |
445 | string[] fileTypes = { "*.conf", "*.json", "*.yaml"};
446 |
447 | _fsWatchers.AddRange(paths
448 | .Where(path => null != path && path.Exists)
449 | .Select(path =>
450 | {
451 | List fileWatchers =
452 | new List();
453 |
454 | foreach(var fileType in fileTypes)
455 | {
456 | var fsWatcher = new FileSystemWatcher(
457 | path.FullName, fileType);
458 |
459 | fsWatcher.NotifyFilter =
460 | NotifyFilters.LastWrite
461 | | NotifyFilters.FileName
462 | | NotifyFilters.DirectoryName;
463 |
464 | fsWatcher.IncludeSubdirectories = true;
465 |
466 | fsWatcher.Changed += fsEventHandler;
467 | fsWatcher.Created += fsEventHandler;
468 | fsWatcher.Deleted += fsEventHandler;
469 | fsWatcher.Error += errorEventHandler;
470 | fsWatcher.Renamed += renamedEventHandler;
471 |
472 | fsWatcher.EnableRaisingEvents = true;
473 |
474 | fileWatchers.Add(fsWatcher);
475 | }
476 |
477 | return fileWatchers;
478 | })
479 | .SelectMany(fileWatchers => fileWatchers));
480 | }
481 | }
482 |
483 | private static void TearDownWatchDirectories()
484 | {
485 | lock (_fsWatcherLock)
486 | {
487 | if (null == _fsWatchers)
488 | {
489 | return;
490 | }
491 |
492 | foreach (var fsWatcher in _fsWatchers)
493 | {
494 | fsWatcher.Dispose();
495 | }
496 |
497 | _fsWatchers = null;
498 | }
499 | }
500 |
501 | ///
502 | /// Remove the specified config file from the ConfigManager
503 | ///
504 | /// The specified config file key
505 | public static void RemoveConfig(string key)
506 | {
507 | Configuration config;
508 |
509 | key = key.Split('\\', '/').LastOrDefault();
510 |
511 | string conf = ".conf";
512 | string json = ".json";
513 | string yaml = ".yaml";
514 |
515 | if (key.EndsWith(conf)
516 | || key.EndsWith(json)
517 | || key.EndsWith(yaml))
518 | {
519 | key = key.Substring(0, key.Length - conf.Length);
520 | }
521 |
522 | if (DevMode)
523 | {
524 | string dev = ".dev";
525 | if (key.EndsWith(dev))
526 | {
527 | key = key.Substring(0, key.Length - dev.Length);
528 | }
529 | }
530 |
531 | _configs.TryRemove(key, out config);
532 |
533 | if (ConfigKeys.ConfigPathsConfig.ToString() == key)
534 | {
535 | TearDownWatchDirectories();
536 | SetupWatchDirectories();
537 | }
538 | }
539 |
540 | private static void HandleUpdate(object sender, FileSystemEventArgs e)
541 | {
542 | RemoveConfig(e.Name);
543 | }
544 |
545 | private static void HandleError(object sender, ErrorEventArgs e)
546 | {
547 | if (null != LogException)
548 | {
549 | LogException(
550 | "Exception thrown by FileSystemWatcher",
551 | e.GetException());
552 | }
553 | }
554 |
555 | private static void HandleRename(object sender, RenamedEventArgs e)
556 | {
557 | RemoveConfig(e.OldName);
558 | RemoveConfig(e.Name);
559 | }
560 |
561 | private static bool FileExists(string path)
562 | {
563 | return File.Exists(GetPath(path));
564 | }
565 |
566 | ///
567 | /// Returns the configuration object of the specified type and name
568 | /// from the in-memory dictionary of the ConfigManager.
569 | ///
570 | /// The type of the configuration object.
571 | ///
572 | /// The name of the configuration
573 | /// object to retrieve.
574 | /// An object of type T with the values in the
575 | /// ConfigManager.
576 | public static T GetConfig(string configName, bool cached = true)
577 | where T : new()
578 | {
579 | Configuration configuration;
580 | _configs.TryGetValue(configName, out configuration);
581 | return configuration == null
582 | ? new T()
583 | : cached
584 | ? (T)configuration.Parsed
585 | : ParseConfig(
586 | configuration.Raw,
587 | configuration.ConfigType);
588 | }
589 |
590 | ///
591 | /// Returns the DateTime that the specified file was last written to.
592 | ///
593 | /// The file path.
594 | ///
595 | /// The DateTime that the specified file was last written to.
596 | ///
597 | public static DateTime GetConfigUpdateTime(string configPath)
598 | {
599 | DateTime t = DateTime.MinValue;
600 | try
601 | {
602 | t = File.GetLastWriteTime(configPath);
603 | }
604 | catch (Exception e)
605 | {
606 | if (null != LogException)
607 | {
608 | LogException(
609 | "ConfigManager Error: could not read file creation time: "
610 | + configPath, e);
611 | }
612 | }
613 |
614 | return t;
615 | }
616 |
617 | ///
618 | /// Reads a relative or absolute file path and returns the contents.
619 | /// Nonrooted file paths are checked relative to the configured
620 | /// search directories.
621 | ///
622 | /// The file path.
623 | /// The contents of the file that was found, or empty string
624 | /// if no file present.
625 | public static string ReadConfig(string configPath)
626 | {
627 | string config = "";
628 | // TJ: Check the configPath
629 | try
630 | {
631 | config = File.ReadAllText(configPath);
632 | }
633 | catch (System.IO.FileNotFoundException)
634 | {
635 | // Ignore
636 | }
637 | catch (Exception e)
638 | {
639 | if (null != LogException)
640 | {
641 | LogException(
642 | "ConfigManager Error: could not read file: "
643 | + configPath, e);
644 | }
645 | }
646 |
647 | return config;
648 | }
649 |
650 | ///
651 | /// Parses json into the specified object type.
652 | ///
653 | /// The object type to return.
654 | /// The raw config contents.
655 | /// The type of config file.
656 | ///
657 | /// The contents of the raw json as the specified object type.
658 | ///
659 | public static T ParseConfig(
660 | string configRaw,
661 | ConfigType configType = ConfigType.Json)
662 | where T : new()
663 | {
664 | T config = default(T);
665 | if (configRaw == null)
666 | {
667 | if (null != Log)
668 | {
669 | Log(string.Format(
670 | "ConfigManager Error: "
671 | + "Cannot deserialize null string for Type {0}",
672 | typeof(T).Name));
673 | }
674 | }
675 | else if (configRaw != string.Empty)
676 | {
677 | switch(configType)
678 | {
679 | case ConfigType.Json:
680 | config = Newtonsoft
681 | .Json
682 | .JsonConvert
683 | .DeserializeObject(configRaw);
684 | break;
685 | case ConfigType.Yaml:
686 | config = _yamlDeserializer.Deserialize(
687 | new StringReader(configRaw));
688 | break;
689 | default:
690 | break;
691 | }
692 |
693 |
694 | if (null == config && null != Log)
695 | {
696 | // Deserializing configraw failed, log configraw
697 | Log(string.Format(
698 | "ConfigManager Error: "
699 | +@"Unable to deserialize string ""{0}"" to Type {1}",
700 | configRaw,
701 | typeof(T).Name));
702 | }
703 | }
704 |
705 | if (config == null)
706 | {
707 | config = new T();
708 | }
709 | return config;
710 | }
711 | }
712 | }
713 |
--------------------------------------------------------------------------------