Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
MoreConfiguration.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2021. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
7#include "Stroika/Foundation/Common/Property.h"
10#include "Stroika/Foundation/Execution/WaitableEvent.h"
11
12#include "MoreConfiguration.h"
13
14using namespace std;
15
16using namespace Stroika::Foundation;
20using namespace Stroika::Foundation::Execution;
21
22using namespace StroikaSample;
23using namespace StroikaSample::AppSettings;
24
25namespace {
26 WaitableEvent sWaitableEvent_; // some thread could be waiting on this, and perform some reactive task when the module settings change
27
28}
29
30/*
31 ********************************************************************************
32 ********** AppSettings::Private_::MoreOptionsData_Storage_IMPL_ ****************
33 ********************************************************************************
34 */
35
36AppSettings::Private_::MoreOptionsData_Storage_IMPL_::MoreOptionsData_Storage_IMPL_ ()
37 : fOptionsFile_{
38 /*
39 * Any module name will do. This will map (by default) to a MyModule.json file in XXX.
40 * If you require a single configuration file 'Main" might be a better module name.
41 * But if you have multiple modules with configuration data, pick a name that matches that module,
42 * and they will all be stored under a folder for all your apps configuration.
43 */
44 "MyModule"sv,
45
46 /*
47 * C++ doesn't have intrinsically enough metadata to effectively serialize deserialize data, but its close.
48 * You have to give it class mappings, and other non-builtin types mappings, so that it can serialize.
49 *
50 * Note - this serializing logic is VERY widely useful outside of configuration - for example it can be used
51 * to provide WebService/REST interfaces, or for debugging/logging output.
52 */
53 [] () -> ObjectVariantMapper {
55 mapper.AddClass<MoreOptionsData_> ({
56 {"Enabled"sv, &MoreOptionsData_::fEnabled},
57 {"Last-Synchronized-At"sv, &MoreOptionsData_::fLastSynchronizedAt},
58 });
59 return mapper;
60 }(),
61
62 /*
63 * Hooks for versioning, to manage as your application evolves and the configuration data changes
64 */
66
67 /*
68 * Hook to decide the folder (and filename pattern) where the configuration data will be stored.
69 *
70 * This defaults to
71 * FileSystem::WellKnownLocations::GetApplicationData () + appName + String{IO::FileSystem::kPathComponentSeperator} + moduleName + suffix
72 * or folder:
73 * "/var/opt/Put-Your-App-Name-Here" or "C:\ProgramData\Put-Your-App-Name-Here"
74 * and this module configuration file would be:
75 * "/var/opt/Put-Your-App-Name-Here/MyModule.json" OR
76 * "C:/ProgramData/Put-Your-App-Name-Here/MyModule.json" OR
77 *
78 * \note - this function does NOT create the 'Put-Your-App-Name-Here' folder first, and will NOT persist
79 * files if this folder does not exist.
80 *
81 * Callers can easily replace the default function provided in OptionsFile::mkFilenameMapper - just
82 * don't call that and provide your own lambda - to create the folder.
83 *
84 * But a better pattern is to create the folder in your application installer, typically.
85 */
86 OptionsFile::mkFilenameMapper ("Put-Your-App-Name-Here"sv)}
87 , fActualCurrentConfigData_{fOptionsFile_.Read<MoreOptionsData_> (MoreOptionsData_{})}
88{
89 Set (fActualCurrentConfigData_); // assure derived data (and changed fields etc) up to date
90}
91MoreOptionsData_ AppSettings::Private_::MoreOptionsData_Storage_IMPL_::Get () const
92{
93 // no locking required here for thread safety.
94 // This is always done inside of a read or a full lock by ModuleGetterSetter
95 return fActualCurrentConfigData_;
96}
97void AppSettings::Private_::MoreOptionsData_Storage_IMPL_::Set (const MoreOptionsData_& v)
98{
99 // no locking required here for thread safety.
100 // This is always done inside of a write lock by ModuleGetterSetter
101 fActualCurrentConfigData_ = v;
102 fOptionsFile_.Write (v);
103}
104
105/*
106 ********************************************************************************
107 *************************** AppSettings::TestUse1 ******************************
108 ********************************************************************************
109 */
110void AppSettings::TestUse1 ()
111{
112 // This will be by far the most common use pattern - just read some field of the configuration object
113 if (gModuleConfiguration.Get ().fEnabled) {
114 // do something
115 }
116}
117
118/*
119 ********************************************************************************
120 *************************** AppSettings::TestUse2 ******************************
121 ********************************************************************************
122 */
123void AppSettings::TestUse2 ()
124{
125 // or read several fields all guaranteed within this same snapshot (not holding a lock duing the action)
126 auto d = gModuleConfiguration.Get ();
127 // lock not held here so configuration could change but this code remains safe and crash free
128 if (d.fEnabled and d.fLastSynchronizedAt) {
129 // do something
130 }
131}
132
133/*
134 ********************************************************************************
135 *************************** AppSettings::TestUse3 ******************************
136 ********************************************************************************
137 */
138void AppSettings::TestUse3 ()
139{
140 if (gModuleConfiguration.Get ().fEnabled) {
141 // a non-atomic update of the entire MoreOptionsData_ object
142 auto n = gModuleConfiguration.Get ();
143 n.fEnabled = false; // change something in 'n' here
144 gModuleConfiguration.Set (n);
145 }
146}
147
148/*
149 ********************************************************************************
150 *************************** AppSettings::TestUse4 ******************************
151 ********************************************************************************
152 */
153void AppSettings::TestUse4 ()
154{
155 // Use Update () to atomically update data
156 // Use the return value to tell if a real change was made (so you can invoke some sort of notification/action)
157 static const Duration kMinTime_ = 2min;
158 if (gModuleConfiguration.Update ([] (const MoreOptionsData_& data) -> optional<MoreOptionsData_> {
159 if (data.fLastSynchronizedAt and *data.fLastSynchronizedAt + kMinTime_ > DateTime::Now ()) {
160 MoreOptionsData_ result = data;
161 result.fLastSynchronizedAt = DateTime::Now ();
162 return result;
163 }
164 return {};
165 })) {
166 sWaitableEvent_.Set (); // e.g. trigger someone to wakeup and used changes? - no global lock held here...
167 }
168}
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
ObjectVariantMapper can be used to map C++ types to and from variant-union types, which can be transp...
nonvirtual void AddClass(const Traversal::Iterable< StructFieldInfo > &fieldDescriptions, const ClassMapperOptions< CLASS > &mapperOptions={})
static const ModuleDataUpgraderType kDefaultUpgrader
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
STL namespace.