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