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"
26 #include "generated_configuration.cc"
29 #define STATWRITER_NAMESPACE config
30 #include "statwriter.h"
31 #undef STATWRITER_NAMESPACE
33 // Used to get a name from a type to use in logging
36 static char* const value;
39 #define TYPENAME(type) \
41 char* const type_name<type>::value = (char* const) #type;
50 Configuration::Configuration() {
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;
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;
64 std::vector<ValueChangedListener*> copyListeners() {
65 std::vector<ValueChangedListener*> copy;
68 changeListener.begin(),
70 std::back_inserter(copy),
71 [](std::unique_ptr<ValueChangedListener>& listener)
72 -> ValueChangedListener* { return listener.get(); });
78 void Configuration::setParameter(const std::string& key, T value) {
79 std::vector<ValueChangedListener*> copy;
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);
88 attributes[key] = std::make_shared<value_t>();
90 attributes[key]->value = value;
91 copy = attributes[key]->copyListeners();
94 for (auto* listener : copy) {
95 listener->valueChanged(key, value);
100 void Configuration::setParameter<const char*>(const std::string& key,
102 setParameter(key, std::string(value));
106 T Configuration::getParameter(const std::string& key) const {
107 LockHolder lh(mutex);
109 const auto iter = attributes.find(key);
110 if (iter == attributes.end()) {
114 T* value = boost::get<T>(&iter->second->value);
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);
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;
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;
147 void Configuration::addAlias(const std::string& key, const std::string& alias) {
148 attributes[alias] = attributes[key];
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);
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);
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);
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.
192 if (iter->second->value != requirement.second) {
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");
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)) {
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);
226 * Internal container of an engine parameter.
228 class ConfigItem: public config_item {
230 ConfigItem(const char *theKey, config_datatype theDatatype) :
233 datatype = theDatatype;
234 value.dt_string = &holder;
241 bool Configuration::parseConfiguration(const char *str,
242 SERVER_HANDLE_V1* sapi) {
243 std::vector<std::unique_ptr<ConfigItem> > config;
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())));
251 // And add support for config files...
252 config.push_back(std::make_unique<ConfigItem>("config_file", DT_CONFIGFILE));
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;
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) {
266 switch (items[ii].datatype) {
268 setParameter(items[ii].key,
269 const_cast<const char*>(
270 *(items[ii].value.dt_string)));
273 setParameter(items[ii].key, *items[ii].value.dt_size);
276 setParameter(items[ii].key,
277 (ssize_t)*items[ii].value.dt_ssize);
280 setParameter(items[ii].key, *items[ii].value.dt_bool);
283 setParameter(items[ii].key, *items[ii].value.dt_float);
286 throw std::logic_error("Configuration::parseConfiguration: "
287 "Unexpected DT_CONFIGFILE element after parse_config");
292 if (items[ii].datatype == DT_STRING) {
293 cb_free(*items[ii].value.dt_string);
301 Configuration::~Configuration() = default;