1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * Copyright 2015 Couchbase, Inc
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "configuration.h"
21 #include "configuration_impl.h"
24 #include "generated_configuration.cc"
27 #define STATWRITER_NAMESPACE config
28 #include "statwriter.h"
29 #undef STATWRITER_NAMESPACE
31 // Used to get a name from a type to use in logging
34 static char* const value;
37 #define TYPENAME(type) \
39 char* const type_name<type>::value = (char* const) #type;
48 Configuration::Configuration() {
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;
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;
62 std::vector<ValueChangedListener*> copyListeners() {
63 std::vector<ValueChangedListener*> copy;
66 changeListener.begin(),
68 std::back_inserter(copy),
69 [](std::unique_ptr<ValueChangedListener>& listener)
70 -> ValueChangedListener* { return listener.get(); });
76 void Configuration::setParameter(const std::string& key, T value) {
77 std::vector<ValueChangedListener*> copy;
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);
86 attributes[key] = std::make_shared<value_t>();
88 attributes[key]->value = value;
89 copy = attributes[key]->copyListeners();
92 for (auto* listener : copy) {
93 listener->valueChanged(key, value);
98 void Configuration::setParameter<const char*>(const std::string& key,
100 setParameter(key, std::string(value));
104 T Configuration::getParameter(const std::string& key) const {
105 LockHolder lh(mutex);
107 const auto iter = attributes.find(key);
108 if (iter == attributes.end()) {
112 T* value = boost::get<T>(&iter->second->value);
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);
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;
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;
145 void Configuration::addAlias(const std::string& key, const std::string& alias) {
146 attributes[alias] = attributes[key];
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);
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);
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);
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.
190 if (iter->second->value != requirement.second) {
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");
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)) {
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);
224 * Internal container of an engine parameter.
226 class ConfigItem: public config_item {
228 ConfigItem(const char *theKey, config_datatype theDatatype) :
231 datatype = theDatatype;
232 value.dt_string = &holder;
239 bool Configuration::parseConfiguration(const char *str,
240 SERVER_HANDLE_V1* sapi) {
241 std::vector<std::unique_ptr<ConfigItem> > config;
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())));
249 // And add support for config files...
250 config.push_back(std::make_unique<ConfigItem>("config_file", DT_CONFIGFILE));
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;
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) {
264 switch (items[ii].datatype) {
266 setParameter(items[ii].key,
267 const_cast<const char*>(
268 *(items[ii].value.dt_string)));
271 setParameter(items[ii].key, *items[ii].value.dt_size);
274 setParameter(items[ii].key,
275 (ssize_t)*items[ii].value.dt_ssize);
278 setParameter(items[ii].key, *items[ii].value.dt_bool);
281 setParameter(items[ii].key, *items[ii].value.dt_float);
284 throw std::logic_error("Configuration::parseConfiguration: "
285 "Unexpected DT_CONFIGFILE element after parse_config");
290 if (items[ii].datatype == DT_STRING) {
291 cb_free(*items[ii].value.dt_string);
299 Configuration::~Configuration() = default;