Streamline configuration.h
[ep-engine.git] / src / configuration.cc
1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2015 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17
18 #include "config.h"
19 #include "configuration.h"
20
21 #include "configuration_impl.h"
22
23 #include "locks.h"
24
25 #ifdef AUTOCONF_BUILD
26 #include "generated_configuration.cc"
27 #endif
28
29 #define STATWRITER_NAMESPACE config
30 #include "statwriter.h"
31 #undef STATWRITER_NAMESPACE
32
33 // Used to get a name from a type to use in logging
34 template <typename T>
35 struct type_name {
36     static char* const value;
37 };
38
39 #define TYPENAME(type) \
40     template <>        \
41     char* const type_name<type>::value = (char* const) #type;
42
43 TYPENAME(bool)
44 TYPENAME(size_t)
45 TYPENAME(ssize_t)
46 TYPENAME(float)
47 TYPENAME(std::string)
48 #undef TYPENAME
49
50 Configuration::Configuration() {
51     initialize();
52 }
53
54 struct Configuration::value_t {
55     std::vector<std::unique_ptr<ValueChangedListener>> changeListener;
56     std::unique_ptr<ValueChangedValidator> validator;
57     std::unique_ptr<Requirement> requirement;
58
59     // At the moment, the order of these template parameters must
60     // match the order of the types in config_datatype. Looking
61     // for a cleaner method.
62     value_variant_t value;
63
64     std::vector<ValueChangedListener*> copyListeners() {
65         std::vector<ValueChangedListener*> copy;
66
67         std::transform(
68                 changeListener.begin(),
69                 changeListener.end(),
70                 std::back_inserter(copy),
71                 [](std::unique_ptr<ValueChangedListener>& listener)
72                         -> ValueChangedListener* { return listener.get(); });
73         return copy;
74     }
75 };
76
77 template <class T>
78 void Configuration::setParameter(const std::string& key, T value) {
79     std::vector<ValueChangedListener*> copy;
80     {
81         std::lock_guard<std::mutex> lh(mutex);
82         auto validator = attributes.find(key);
83         if (validator != attributes.end()) {
84             if (validator->second->validator) {
85                 validator->second->validator->validate(key, value);
86             }
87         } else {
88             attributes[key] = std::make_shared<value_t>();
89         }
90         attributes[key]->value = value;
91         copy = attributes[key]->copyListeners();
92     }
93
94     for (auto* listener : copy) {
95         listener->valueChanged(key, value);
96     }
97 }
98
99 template <>
100 void Configuration::setParameter<const char*>(const std::string& key,
101                                               const char* value) {
102     setParameter(key, std::string(value));
103 }
104
105 template <class T>
106 T Configuration::getParameter(const std::string& key) const {
107     LockHolder lh(mutex);
108
109     const auto iter = attributes.find(key);
110     if (iter == attributes.end()) {
111         return T();
112     }
113
114     T* value = boost::get<T>(&iter->second->value);
115
116     if (value == nullptr) {
117         std::cerr << iter->second->value << std::endl;
118         throw std::invalid_argument(
119                 "Configuration::getParameter: key \"" + key + "\" (which is " +
120                 std::to_string(iter->second->value.which()) + ") is not " +
121                 type_name<T>::value);
122     }
123     return *value;
124 }
125
126 template bool Configuration::getParameter<bool>(const std::string& key) const;
127 template size_t Configuration::getParameter<size_t>(
128         const std::string& key) const;
129 template ssize_t Configuration::getParameter<ssize_t>(
130         const std::string& key) const;
131 template float Configuration::getParameter<float>(const std::string& key) const;
132 template std::string Configuration::getParameter<std::string>(
133         const std::string& key) const;
134
135 std::ostream& operator<<(std::ostream& out, const Configuration& config) {
136     LockHolder lh(config.mutex);
137     for (const auto& attribute : config.attributes) {
138         std::stringstream line;
139         line << std::boolalpha << attribute.first.c_str() << " = ["
140              << attribute.second->value << "]" << std::endl;
141         out << line.str();
142     }
143
144     return out;
145 }
146
147 void Configuration::addAlias(const std::string& key, const std::string& alias) {
148     attributes[alias] = attributes[key];
149 }
150
151 void Configuration::addValueChangedListener(const std::string &key,
152                                             ValueChangedListener *val) {
153     LockHolder lh(mutex);
154     if (attributes.find(key) != attributes.end()) {
155         attributes[key]->changeListener.emplace_back(val);
156     }
157 }
158
159 ValueChangedValidator *Configuration::setValueValidator(const std::string &key,
160                                             ValueChangedValidator *validator) {
161     ValueChangedValidator *ret = nullptr;
162     LockHolder lh(mutex);
163     if (attributes.find(key) != attributes.end()) {
164         ret = attributes[key]->validator.release();
165         attributes[key]->validator.reset(validator);
166     }
167
168     return ret;
169 }
170
171 Requirement* Configuration::setRequirements(const std::string& key,
172                                             Requirement* requirement) {
173     Requirement* ret = nullptr;
174     LockHolder lh(mutex);
175     if (attributes.find(key) != attributes.end()) {
176         ret = attributes[key]->requirement.release();
177         attributes[key]->requirement.reset(requirement);
178     }
179
180     return ret;
181 }
182
183 bool Configuration::requirementsMet(const value_t& value) const {
184     if (value.requirement) {
185         for (auto requirement : value.requirement->requirements) {
186             const auto iter = attributes.find(requirement.first);
187             if (iter == attributes.end()) {
188                 // Parameter does not exist, returning true assuming the config
189                 // is not yet complete. We cannot verify yet.
190                 return true;
191             }
192             if (iter->second->value != requirement.second) {
193                 return false;
194             }
195         }
196     }
197     return true;
198 }
199 void Configuration::requirementsMetOrThrow(const std::string& key) const {
200     LockHolder lh(mutex);
201     auto itr = attributes.find(key);
202     if (itr != attributes.end()) {
203         if (!requirementsMet(*(itr->second))) {
204             throw requirements_unsatisfied("Cannot set" + key +
205                                            " : requirements not met");
206         }
207     }
208 }
209
210 void Configuration::addStats(ADD_STAT add_stat, const void *c) const {
211     LockHolder lh(mutex);
212     for (const auto& attribute :  attributes) {
213         if (!requirementsMet(*attribute.second)) {
214             continue;
215         }
216         std::stringstream value;
217         value << std::boolalpha << attribute.second->value;
218         std::stringstream key;
219         key << "ep_" << attribute.first;
220         std::string k = key.str();
221         add_casted_stat(k.c_str(), value.str().data(), add_stat, c);
222     }
223 }
224
225 /**
226  * Internal container of an engine parameter.
227  */
228 class ConfigItem: public config_item {
229 public:
230     ConfigItem(const char *theKey, config_datatype theDatatype) :
231                                                                 holder(NULL) {
232         key = theKey;
233         datatype = theDatatype;
234         value.dt_string = &holder;
235     }
236
237 private:
238     char *holder;
239 };
240
241 bool Configuration::parseConfiguration(const char *str,
242                                        SERVER_HANDLE_V1* sapi) {
243     std::vector<std::unique_ptr<ConfigItem> > config;
244
245     for (const auto& attribute : attributes) {
246         config.push_back(std::make_unique<ConfigItem>(
247                 attribute.first.c_str(),
248                 config_datatype(attribute.second->value.which())));
249     }
250
251     // And add support for config files...
252     config.push_back(std::make_unique<ConfigItem>("config_file", DT_CONFIGFILE));
253
254     const int nelem = config.size();
255     std::vector<config_item> items(nelem + 1);
256     for (int ii = 0; ii < nelem; ++ii) {
257         items[ii].key = config[ii]->key;
258         items[ii].datatype = config[ii]->datatype;
259         items[ii].value.dt_string = config[ii]->value.dt_string;
260     }
261
262     bool ret = sapi->core->parse_config(str, items.data(), stderr) == 0;
263     for (int ii = 0; ii < nelem; ++ii) {
264         if (items[ii].found) {
265             if (ret) {
266                 switch (items[ii].datatype) {
267                 case DT_STRING:
268                     setParameter(items[ii].key,
269                                  const_cast<const char*>(
270                                          *(items[ii].value.dt_string)));
271                     break;
272                 case DT_SIZE:
273                     setParameter(items[ii].key, *items[ii].value.dt_size);
274                     break;
275                 case DT_SSIZE:
276                     setParameter(items[ii].key,
277                                  (ssize_t)*items[ii].value.dt_ssize);
278                     break;
279                 case DT_BOOL:
280                     setParameter(items[ii].key, *items[ii].value.dt_bool);
281                     break;
282                 case DT_FLOAT:
283                     setParameter(items[ii].key, *items[ii].value.dt_float);
284                     break;
285                 case DT_CONFIGFILE:
286                     throw std::logic_error("Configuration::parseConfiguration: "
287                             "Unexpected DT_CONFIGFILE element after parse_config");
288                     break;
289                 }
290             }
291
292             if (items[ii].datatype == DT_STRING) {
293                 cb_free(*items[ii].value.dt_string);
294             }
295         }
296     }
297
298     return ret;
299 }
300
301 Configuration::~Configuration() = default;