#include "config.h"
#include "configuration.h"
-#include <boost/variant.hpp>
-#include <platform/cb_malloc.h>
-
-#include <algorithm>
-#include <sstream>
-#include <vector>
+#include "configuration_impl.h"
#ifdef AUTOCONF_BUILD
#include "generated_configuration.cc"
struct Configuration::value_t {
std::vector<std::unique_ptr<ValueChangedListener>> changeListener;
std::unique_ptr<ValueChangedValidator> validator;
+ std::unique_ptr<Requirement> requirement;
// At the moment, the order of these template parameters must
// match the order of the types in config_datatype. Looking
// for a cleaner method.
- boost::variant<size_t, ssize_t, float, bool, std::string> value;
+ value_variant_t value;
std::vector<ValueChangedListener*> copyListeners() {
std::vector<ValueChangedListener*> copy;
return ret;
}
+Requirement* Configuration::setRequirements(const std::string& key,
+ Requirement* requirement) {
+ Requirement* ret = nullptr;
+ LockHolder lh(mutex);
+ if (attributes.find(key) != attributes.end()) {
+ ret = attributes[key]->requirement.release();
+ attributes[key]->requirement.reset(requirement);
+ }
+
+ return ret;
+}
+
+bool Configuration::requirementsMet(const value_t& value) const {
+ if (value.requirement) {
+ for (auto requirement : value.requirement->requirements) {
+ const auto iter = attributes.find(requirement.first);
+ if (iter == attributes.end()) {
+ // Parameter does not exist, returning true assuming the config
+ // is not yet complete. We cannot verify yet.
+ return true;
+ }
+ if (iter->second->value != requirement.second) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+void Configuration::requirementsMetOrThrow(const std::string& key) const {
+ LockHolder lh(mutex);
+ auto itr = attributes.find(key);
+ if (itr != attributes.end()) {
+ if (!requirementsMet(*(itr->second))) {
+ throw requirements_unsatisfied("Cannot set" + key +
+ " : requirements not met");
+ }
+ }
+}
+
void Configuration::addStats(ADD_STAT add_stat, const void *c) const {
LockHolder lh(mutex);
for (const auto& attribute : attributes) {
+ if (!requirementsMet(*attribute.second)) {
+ continue;
+ }
std::stringstream value;
value << std::boolalpha << attribute.second->value;
std::stringstream key;
std::set<std::string> acceptable;
};
+class Requirement;
+
/**
* The configuration class represents and provides access to the
* entire configuration of the server.
*/
class Configuration {
public:
+ struct value_t;
+
Configuration();
~Configuration();
*/
void addAlias(const std::string& key, const std::string& alias);
+ /**
+ * Adds a prerequisite to a configuration option. This must be satisfied
+ * in order to set/get the config value or for it to appear in stats.
+ *
+ * @param key the key to set the requirement for
+ * @param requirement the requirement
+ */
+ Requirement* setRequirements(const std::string& key,
+ Requirement* requirement);
+
+ bool requirementsMet(const value_t& value) const;
+
+ void requirementsMetOrThrow(const std::string& key) const;
+
protected:
/**
* Set the configuration parameter for a given key to
private:
void initialize();
- struct value_t;
// Access to the configuration variables is protected by the mutex
mutable std::mutex mutex;
--- /dev/null
+/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2017 Couchbase, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Contains implementations relating to configuration.h that are not needed in
+ * most places Configuration is. This can be included in a far smaller number
+ * of places, reducing the overhead of including configuration.h.
+ */
+
+#pragma once
+
+#include <boost/variant.hpp>
+
+#include <string>
+#include <vector>
+
+using value_variant_t =
+ boost::variant<size_t, ssize_t, float, bool, std::string>;
+
+class requirements_unsatisfied : public std::logic_error {
+public:
+ requirements_unsatisfied(const std::string& msg) : std::logic_error(msg) {
+ }
+};
+
+class Requirement {
+public:
+ Requirement* add(const std::string& key, value_variant_t value) {
+ requirements.emplace_back(key, value);
+ return this;
+ }
+
+ std::vector<std::pair<std::string, value_variant_t>> requirements;
+};
\ No newline at end of file
<< endl
<< "#include \"config.h\"" << endl
<< "#include \"configuration.h\"" << endl
+ << "#include \"configuration_impl.h\"" << endl
<< "#include <platform/sysinfo.h>" << endl;
validators["range"] = getRangeValidatorCode;
validators["enum"] = getEnumValidatorCode;
return (iter->second)(key, o);
}
+/**
+ * Generates code from the requirements field.
+ *
+ * Generates code to be used in generated_configuration.cc constructing the
+ * Requirement object and adding the appropriate requirements.
+ * @param key key to generate requirements for
+ * @param o json object representing the config parameter
+ * @param params json object of all parameters, required to determine the
+ * intended type of the required parameter.
+ * @return string of the code constructing a Requirement object.
+ */
+static string getRequirements(const std::string& key, cJSON* o, cJSON* params) {
+ if (o == NULL) {
+ return "";
+ }
+
+ cJSON* requirements = cJSON_GetObjectItem(o, "requires");
+
+ if (requirements == NULL) {
+ return "";
+ }
+
+ ssize_t num = cJSON_GetArraySize(requirements);
+ if (num <= 0) {
+ return "";
+ }
+
+ std::ostringstream ss;
+
+ ss << "(new Requirement)\n";
+
+ for (int ii = 0; ii < num; ++ii) {
+ cJSON* req = cJSON_GetArrayItem(requirements, ii);
+ char* req_key = req->string;
+
+ cJSON* req_param = cJSON_GetObjectItem(params, req_key);
+
+ if (req_param == NULL) {
+ cerr << "Required parameter \"" << req_key << "\" for parameter \""
+ << key << "\" does not exist" << endl;
+ exit(1);
+ }
+
+ string type = getDatatype(req_key, req_param);
+ string value;
+
+ switch (req->type) {
+ case cJSON_String:
+ value = std::string("\"") + req->valuestring + "\"";
+ break;
+ case cJSON_Number:
+ if (type == "float") {
+ value = std::to_string(req->valuedouble);
+ } else {
+ value = std::to_string(req->valueint);
+ }
+ break;
+ case cJSON_True:
+ value = "true";
+ break;
+ case cJSON_False:
+ value = "false";
+ break;
+ }
+
+ ss << " ->add(\"" << req_key << "\", (" << type << ")" << value
+ << ")";
+ }
+
+ return ss.str();
+}
+
static string getGetterPrefix(const string &str) {
if (str.compare("bool") == 0) {
return "is";
return ss.str();
}
-static void generate(cJSON *o) {
+static void generate(cJSON* o, cJSON* params) {
cb_assert(o != NULL);
string config_name = o->string;
}
string validator = getValidator(config_name, o);
+ string requirements = getRequirements(config_name, o, params);
// Generate prototypes
prototypes << " " << type
initialization << " setValueValidator(\"" << config_name
<< "\", " << validator << ");" << endl;
}
+ if (!requirements.empty()) {
+ initialization << " setRequirements(\"" << config_name << "\", "
+ << requirements << ");" << endl;
+ }
if (hasAliases(o)) {
for (std::string alias : getAliases(o)) {
initialization << " addAlias(\"" << config_name << "\", \""
int num = cJSON_GetArraySize(params);
for (int ii = 0; ii < num; ++ii) {
- generate(cJSON_GetArrayItem(params, ii));
+ generate(cJSON_GetArrayItem(params, ii), params);
}
prototypes << "#endif // SRC_GENERATED_CONFIGURATION_H_" << endl;