<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2015 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.
 */

#include "config.h"

#include <algorithm>
#include <iostream>
#include <limits>
#include <sstream>
#include <vector>

#include "configuration.h"

#ifdef AUTOCONF_BUILD
#include "generated_configuration.cc"
#endif

#define STATWRITER_NAMESPACE config
#include "statwriter.h"
#undef STATWRITER_NAMESPACE

Configuration::Configuration() {
    initialize();
}

std::string Configuration::getString(const std::string &key) const {
    Mutex *ptr = const_cast<Mutex*> (&mutex);
    LockHolder lh(*ptr);

    std::map<std::string, value_t>::const_iterator iter;
    if ((iter = attributes.find(key)) == attributes.end()) {
        return std::string();
    }
    if (iter->second.datatype != DT_STRING) {
        throw std::invalid_argument("Configuration::getString: key (which is " +
                        std::to_string(iter->second.datatype) +
                        ") is not DT_STRING");
    }

    if (iter->second.val.v_string) {
        return std::string(iter->second.val.v_string);
    }
    return std::string();
}

bool Configuration::getBool(const std::string &key) const {
    Mutex *ptr = const_cast<Mutex*> (&mutex);
    LockHolder lh(*ptr);

    std::map<std::string, value_t>::const_iterator iter;
    if ((iter = attributes.find(key)) == attributes.end()) {
        return false;
    }
    if (iter->second.datatype != DT_BOOL) {
        throw std::invalid_argument("Configuration::getBool: key (which is " +
                        std::to_string(iter->second.datatype) +
                        ") is not DT_BOOL");
    }
    return iter->second.val.v_bool;
}

float Configuration::getFloat(const std::string &key) const {
    Mutex *ptr = const_cast<Mutex*> (&mutex);
    LockHolder lh(*ptr);

    std::map<std::string, value_t>::const_iterator iter;
    if ((iter = attributes.find(key)) == attributes.end()) {
        return 0;
    }
    if (iter->second.datatype != DT_FLOAT) {
        throw std::invalid_argument("Configuration::getFloat: key (which is " +
                        std::to_string(iter->second.datatype) +
                        ") is not DT_FLOAT");
    }
    return iter->second.val.v_float;
}

size_t Configuration::getInteger(const std::string &key) const {
    Mutex *ptr = const_cast<Mutex*> (&mutex);
    LockHolder lh(*ptr);

    std::map<std::string, value_t>::const_iterator iter;
    if ((iter = attributes.find(key)) == attributes.end()) {
        return 0;
    }
    if (iter->second.datatype != DT_SIZE) {
        throw std::invalid_argument("Configuration::getInteger: key (which is " +
                        std::to_string(iter->second.datatype) +
                        ") is not DT_SIZE");
    }
    return iter->second.val.v_size;
}

ssize_t Configuration::getSignedInteger(const std::string &key) const {
    Mutex *ptr = const_cast<Mutex*> (&mutex);
    LockHolder lh(*ptr);

    std::map<std::string, value_t>::const_iterator iter;
    if ((iter = attributes.find(key)) == attributes.end()) {
        return 0;
    }
    if (iter->second.datatype != DT_SSIZE) {
        throw std::invalid_argument("Configuration::getSignedInteger: key "
                        "(which is " + std::to_string(iter->second.datatype) +
                        ") is not DT_SSIZE");
    }
    return iter->second.val.v_ssize;
}

std::ostream& operator <<(std::ostream &out, const Configuration &config) {
    LockHolder lh(const_cast<Mutex&> (config.mutex));
    std::map<std::string, Configuration::value_t>::const_iterator iter;
    for (iter = config.attributes.begin(); iter != config.attributes.end();
        ++iter) {
        std::stringstream line;
        line << iter->first.c_str();
        line << " = [";
        switch (iter->second.datatype) {
        case DT_BOOL:
            if (iter->second.val.v_bool) {
                line << "true";
            } else {
                line << "false";
            }
            break;
        case DT_STRING:
            line << iter->second.val.v_string;
            break;
        case DT_SIZE:
            line << iter->second.val.v_size;
            break;
        case DT_SSIZE:
            line << iter->second.val.v_ssize;
            break;
        case DT_FLOAT:
            line << iter->second.val.v_float;
            break;
        case DT_CONFIGFILE:
            continue;
        default:
            // ignore
            ;
        }
        line << "]" << std::endl;
        out << line.str();
    }

    return out;
}

void Configuration::setParameter(const std::string &key, bool value) {
    LockHolder lh(mutex);
    std::map<std::string, value_t>::iterator validator = attributes.find(key);
    if (validator != attributes.end()) {
        if (validator->second.validator != NULL) {
            validator->second.validator->validateBool(key, value);
        }
    }
    attributes[key].datatype = DT_BOOL;
    attributes[key].val.v_bool = value;
    std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
    lh.unlock();
    std::vector<ValueChangedListener*>::iterator iter;
    for (iter = copy.begin(); iter != copy.end(); ++iter) {
        (*iter)->booleanValueChanged(key, value);
    }
}

void Configuration::setParameter(const std::string &key, size_t value) {
    LockHolder lh(mutex);
    std::map<std::string, value_t>::iterator validator = attributes.find(key);
    if (validator != attributes.end()) {
        if (validator->second.validator != NULL) {
            validator->second.validator->validateSize(key, value);
        }
    }
    attributes[key].datatype = DT_SIZE;
    if (key.compare("cache_size") == 0) {
        attributes["max_size"].val.v_size = value;
    } else {
        attributes[key].val.v_size = value;
    }

    std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
    lh.unlock();
    std::vector<ValueChangedListener*>::iterator iter;
    for (iter = copy.begin(); iter != copy.end(); ++iter) {
        (*iter)->sizeValueChanged(key, value);
    }
}

void Configuration::setParameter(const std::string &key, ssize_t value) {
    LockHolder lh(mutex);
    std::map<std::string, value_t>::iterator validator = attributes.find(key);
    if (validator != attributes.end()) {
        if (validator->second.validator != NULL) {
            validator->second.validator->validateSSize(key, value);
        }
    }
    attributes[key].datatype = DT_SSIZE;
    if (key.compare("cache_size") == 0) {
        attributes["max_size"].val.v_ssize = value;
    } else {
        attributes[key].val.v_ssize = value;
    }

    std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
    lh.unlock();
    std::vector<ValueChangedListener*>::iterator iter;
    for (iter = copy.begin(); iter != copy.end(); ++iter) {
        (*iter)->sizeValueChanged(key, value);
    }
}

void Configuration::setParameter(const std::string &key, float value) {
    LockHolder lh(mutex);

    std::map<std::string, value_t>::iterator validator = attributes.find(key);
    if (validator != attributes.end()) {
        if (validator->second.validator != NULL) {
            validator->second.validator->validateFloat(key, value);
        }
    }

    attributes[key].datatype = DT_FLOAT;
    attributes[key].val.v_float = value;
    std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
    lh.unlock();
    std::vector<ValueChangedListener*>::iterator iter;
    for (iter = copy.begin(); iter != copy.end(); ++iter) {
        (*iter)->floatValueChanged(key, value);
    }
}

void Configuration::setParameter(const std::string &key,
                                 const std::string &value) {
    if (value.length() == 0) {
        setParameter(key, (const char *)NULL);
    } else {
        setParameter(key, value.c_str());
    }
}

void Configuration::setParameter(const std::string &key, const char *value) {
    LockHolder lh(mutex);
    std::map<std::string, value_t>::iterator validator = attributes.find(key);
    if (validator != attributes.end()) {
        if (validator->second.validator != NULL) {
            validator->second.validator->validateString(key, value);
        }
    }

    if (attributes.find(key) != attributes.end() && attributes[key].datatype
            == DT_STRING) {
        free((void*)attributes[key].val.v_string);
    }
    attributes[key].datatype = DT_STRING;
    attributes[key].val.v_string = NULL;
    if (value != NULL) {
        attributes[key].val.v_string = strdup(value);
    }

    std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
    lh.unlock();
    std::vector<ValueChangedListener*>::iterator iter;
    for (iter = copy.begin(); iter != copy.end(); ++iter) {
        (*iter)->stringValueChanged(key, value);
    }
}

void Configuration::addValueChangedListener(const std::string &key,
                                            ValueChangedListener *val) {
    LockHolder lh(mutex);
    if (attributes.find(key) != attributes.end()) {
        attributes[key].changeListener.push_back(val);
    }
}

ValueChangedValidator *Configuration::setValueValidator(const std::string &key,
                                            ValueChangedValidator *validator) {
    ValueChangedValidator *ret = nullptr;
    LockHolder lh(mutex);
    if (attributes.find(key) != attributes.end()) {
        ret = attributes[key].validator;
        attributes[key].validator = validator;
    }

    return ret;
}

void Configuration::addStats(ADD_STAT add_stat, const void *c) const {
    LockHolder lh(const_cast<Mutex&> (mutex));
    std::map<std::string, value_t>::const_iterator iter;
    for (iter = attributes.begin(); iter != attributes.end(); ++iter) {
        std::stringstream value;
        switch (iter->second.datatype) {
        case DT_BOOL:
            value << std::boolalpha << iter->second.val.v_bool << std::noboolalpha;
            break;
        case DT_STRING:
            value << iter->second.val.v_string;
            break;
        case DT_SIZE:
            value << iter->second.val.v_size;
            break;
        case DT_SSIZE:
            value << iter->second.val.v_ssize;
            break;
        case DT_FLOAT:
            value << iter->second.val.v_float;
            break;
        case DT_CONFIGFILE:
        default:
            // ignore
            ;
        }

        std::stringstream key;
        key << "ep_" << iter->first;
        std::string k = key.str();
        add_casted_stat(k.c_str(), value.str().data(), add_stat, c);
    }
}

/**
 * Internal container of an engine parameter.
 */
class ConfigItem: public config_item {
public:
    ConfigItem(const char *theKey, config_datatype theDatatype) :
                                                                holder(NULL) {
        key = theKey;
        datatype = theDatatype;
        value.dt_string = &holder;
    }

private:
    char *holder;
};

bool Configuration::parseConfiguration(const char *str,
                                       SERVER_HANDLE_V1* sapi) {
    std::vector<ConfigItem *> config;

    std::map<std::string, value_t>::const_iterator iter;
    for (iter = attributes.begin(); iter != attributes.end(); ++iter) {
        config.push_back(new ConfigItem(iter->first.c_str(),
                                        iter->second.datatype));
    }

    // we don't have a good support for alias yet...
    config.push_back(new ConfigItem("cache_size", DT_SIZE));

    // And add support for config files...
    config.push_back(new ConfigItem("config_file", DT_CONFIGFILE));

    struct config_item *items = (struct config_item*)calloc(config.size() + 1,
            sizeof(struct config_item));
    int nelem = config.size();
    for (int ii = 0; ii < nelem; ++ii) {
        items[ii].key = config[ii]->key;
        items[ii].datatype = config[ii]->datatype;
        items[ii].value.dt_string = config[ii]->value.dt_string;
    }

    bool ret = sapi->core->parse_config(str, items, stderr) == 0;
    for (int ii = 0; ii < nelem; ++ii) {
        if (items[ii].found) {
            if (ret) {
                switch (items[ii].datatype) {
                case DT_STRING:
                    setParameter(items[ii].key, *(items[ii].value.dt_string));
                    break;
                case DT_SIZE:
                    setParameter(items[ii].key, *items[ii].value.dt_size);
                    break;
                case DT_SSIZE:
                    setParameter(items[ii].key,
                                 (ssize_t)*items[ii].value.dt_ssize);
                    break;
                case DT_BOOL:
                    setParameter(items[ii].key, *items[ii].value.dt_bool);
                    break;
                case DT_FLOAT:
                    setParameter(items[ii].key, *items[ii].value.dt_float);
                    break;
                default:
                    abort();
                }
            }

            if (items[ii].datatype == DT_STRING) {
                free(*items[ii].value.dt_string);
            }
        }
    }

    // release allocated memory..
    free(items);
    std::vector<ConfigItem *>::iterator ii;
    for (ii = config.begin(); ii != config.end(); ++ii) {
        delete *ii;
    }

    return ret;
}

Configuration::~Configuration() {
    std::map<std::string, value_t>::iterator iter;
    for (iter = attributes.begin(); iter != attributes.end(); ++iter) {
        std::vector<ValueChangedListener*>::iterator ii;
        for (ii = iter->second.changeListener.begin();
             ii != iter->second.changeListener.end(); ++ii) {
            delete *ii;
        }

        delete iter->second.validator;
        if (iter->second.datatype == DT_STRING) {
            free((void*)iter->second.val.v_string);
        }
    }
}
<commit_msg>MB-20586: Configuration: Reduce use of C-style memory allocation<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2015 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.
 */

#include "config.h"

#include <algorithm>
#include <iostream>
#include <limits>
#include <sstream>
#include <vector>

#include "configuration.h"

#ifdef AUTOCONF_BUILD
#include "generated_configuration.cc"
#endif

#define STATWRITER_NAMESPACE config
#include "statwriter.h"
#undef STATWRITER_NAMESPACE

Configuration::Configuration() {
    initialize();
}

std::string Configuration::getString(const std::string &key) const {
    Mutex *ptr = const_cast<Mutex*> (&mutex);
    LockHolder lh(*ptr);

    std::map<std::string, value_t>::const_iterator iter;
    if ((iter = attributes.find(key)) == attributes.end()) {
        return std::string();
    }
    if (iter->second.datatype != DT_STRING) {
        throw std::invalid_argument("Configuration::getString: key (which is " +
                        std::to_string(iter->second.datatype) +
                        ") is not DT_STRING");
    }

    if (iter->second.val.v_string) {
        return std::string(iter->second.val.v_string);
    }
    return std::string();
}

bool Configuration::getBool(const std::string &key) const {
    Mutex *ptr = const_cast<Mutex*> (&mutex);
    LockHolder lh(*ptr);

    std::map<std::string, value_t>::const_iterator iter;
    if ((iter = attributes.find(key)) == attributes.end()) {
        return false;
    }
    if (iter->second.datatype != DT_BOOL) {
        throw std::invalid_argument("Configuration::getBool: key (which is " +
                        std::to_string(iter->second.datatype) +
                        ") is not DT_BOOL");
    }
    return iter->second.val.v_bool;
}

float Configuration::getFloat(const std::string &key) const {
    Mutex *ptr = const_cast<Mutex*> (&mutex);
    LockHolder lh(*ptr);

    std::map<std::string, value_t>::const_iterator iter;
    if ((iter = attributes.find(key)) == attributes.end()) {
        return 0;
    }
    if (iter->second.datatype != DT_FLOAT) {
        throw std::invalid_argument("Configuration::getFloat: key (which is " +
                        std::to_string(iter->second.datatype) +
                        ") is not DT_FLOAT");
    }
    return iter->second.val.v_float;
}

size_t Configuration::getInteger(const std::string &key) const {
    Mutex *ptr = const_cast<Mutex*> (&mutex);
    LockHolder lh(*ptr);

    std::map<std::string, value_t>::const_iterator iter;
    if ((iter = attributes.find(key)) == attributes.end()) {
        return 0;
    }
    if (iter->second.datatype != DT_SIZE) {
        throw std::invalid_argument("Configuration::getInteger: key (which is " +
                        std::to_string(iter->second.datatype) +
                        ") is not DT_SIZE");
    }
    return iter->second.val.v_size;
}

ssize_t Configuration::getSignedInteger(const std::string &key) const {
    Mutex *ptr = const_cast<Mutex*> (&mutex);
    LockHolder lh(*ptr);

    std::map<std::string, value_t>::const_iterator iter;
    if ((iter = attributes.find(key)) == attributes.end()) {
        return 0;
    }
    if (iter->second.datatype != DT_SSIZE) {
        throw std::invalid_argument("Configuration::getSignedInteger: key "
                        "(which is " + std::to_string(iter->second.datatype) +
                        ") is not DT_SSIZE");
    }
    return iter->second.val.v_ssize;
}

std::ostream& operator <<(std::ostream &out, const Configuration &config) {
    LockHolder lh(const_cast<Mutex&> (config.mutex));
    std::map<std::string, Configuration::value_t>::const_iterator iter;
    for (iter = config.attributes.begin(); iter != config.attributes.end();
        ++iter) {
        std::stringstream line;
        line << iter->first.c_str();
        line << " = [";
        switch (iter->second.datatype) {
        case DT_BOOL:
            if (iter->second.val.v_bool) {
                line << "true";
            } else {
                line << "false";
            }
            break;
        case DT_STRING:
            line << iter->second.val.v_string;
            break;
        case DT_SIZE:
            line << iter->second.val.v_size;
            break;
        case DT_SSIZE:
            line << iter->second.val.v_ssize;
            break;
        case DT_FLOAT:
            line << iter->second.val.v_float;
            break;
        case DT_CONFIGFILE:
            continue;
        default:
            // ignore
            ;
        }
        line << "]" << std::endl;
        out << line.str();
    }

    return out;
}

void Configuration::setParameter(const std::string &key, bool value) {
    LockHolder lh(mutex);
    std::map<std::string, value_t>::iterator validator = attributes.find(key);
    if (validator != attributes.end()) {
        if (validator->second.validator != NULL) {
            validator->second.validator->validateBool(key, value);
        }
    }
    attributes[key].datatype = DT_BOOL;
    attributes[key].val.v_bool = value;
    std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
    lh.unlock();
    std::vector<ValueChangedListener*>::iterator iter;
    for (iter = copy.begin(); iter != copy.end(); ++iter) {
        (*iter)->booleanValueChanged(key, value);
    }
}

void Configuration::setParameter(const std::string &key, size_t value) {
    LockHolder lh(mutex);
    std::map<std::string, value_t>::iterator validator = attributes.find(key);
    if (validator != attributes.end()) {
        if (validator->second.validator != NULL) {
            validator->second.validator->validateSize(key, value);
        }
    }
    attributes[key].datatype = DT_SIZE;
    if (key.compare("cache_size") == 0) {
        attributes["max_size"].val.v_size = value;
    } else {
        attributes[key].val.v_size = value;
    }

    std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
    lh.unlock();
    std::vector<ValueChangedListener*>::iterator iter;
    for (iter = copy.begin(); iter != copy.end(); ++iter) {
        (*iter)->sizeValueChanged(key, value);
    }
}

void Configuration::setParameter(const std::string &key, ssize_t value) {
    LockHolder lh(mutex);
    std::map<std::string, value_t>::iterator validator = attributes.find(key);
    if (validator != attributes.end()) {
        if (validator->second.validator != NULL) {
            validator->second.validator->validateSSize(key, value);
        }
    }
    attributes[key].datatype = DT_SSIZE;
    if (key.compare("cache_size") == 0) {
        attributes["max_size"].val.v_ssize = value;
    } else {
        attributes[key].val.v_ssize = value;
    }

    std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
    lh.unlock();
    std::vector<ValueChangedListener*>::iterator iter;
    for (iter = copy.begin(); iter != copy.end(); ++iter) {
        (*iter)->sizeValueChanged(key, value);
    }
}

void Configuration::setParameter(const std::string &key, float value) {
    LockHolder lh(mutex);

    std::map<std::string, value_t>::iterator validator = attributes.find(key);
    if (validator != attributes.end()) {
        if (validator->second.validator != NULL) {
            validator->second.validator->validateFloat(key, value);
        }
    }

    attributes[key].datatype = DT_FLOAT;
    attributes[key].val.v_float = value;
    std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
    lh.unlock();
    std::vector<ValueChangedListener*>::iterator iter;
    for (iter = copy.begin(); iter != copy.end(); ++iter) {
        (*iter)->floatValueChanged(key, value);
    }
}

void Configuration::setParameter(const std::string &key,
                                 const std::string &value) {
    if (value.length() == 0) {
        setParameter(key, (const char *)NULL);
    } else {
        setParameter(key, value.c_str());
    }
}

void Configuration::setParameter(const std::string &key, const char *value) {
    LockHolder lh(mutex);
    std::map<std::string, value_t>::iterator validator = attributes.find(key);
    if (validator != attributes.end()) {
        if (validator->second.validator != NULL) {
            validator->second.validator->validateString(key, value);
        }
    }

    if (attributes.find(key) != attributes.end() && attributes[key].datatype
            == DT_STRING) {
        free((void*)attributes[key].val.v_string);
    }
    attributes[key].datatype = DT_STRING;
    attributes[key].val.v_string = NULL;
    if (value != NULL) {
        attributes[key].val.v_string = strdup(value);
    }

    std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
    lh.unlock();
    std::vector<ValueChangedListener*>::iterator iter;
    for (iter = copy.begin(); iter != copy.end(); ++iter) {
        (*iter)->stringValueChanged(key, value);
    }
}

void Configuration::addValueChangedListener(const std::string &key,
                                            ValueChangedListener *val) {
    LockHolder lh(mutex);
    if (attributes.find(key) != attributes.end()) {
        attributes[key].changeListener.push_back(val);
    }
}

ValueChangedValidator *Configuration::setValueValidator(const std::string &key,
                                            ValueChangedValidator *validator) {
    ValueChangedValidator *ret = nullptr;
    LockHolder lh(mutex);
    if (attributes.find(key) != attributes.end()) {
        ret = attributes[key].validator;
        attributes[key].validator = validator;
    }

    return ret;
}

void Configuration::addStats(ADD_STAT add_stat, const void *c) const {
    LockHolder lh(const_cast<Mutex&> (mutex));
    std::map<std::string, value_t>::const_iterator iter;
    for (iter = attributes.begin(); iter != attributes.end(); ++iter) {
        std::stringstream value;
        switch (iter->second.datatype) {
        case DT_BOOL:
            value << std::boolalpha << iter->second.val.v_bool << std::noboolalpha;
            break;
        case DT_STRING:
            value << iter->second.val.v_string;
            break;
        case DT_SIZE:
            value << iter->second.val.v_size;
            break;
        case DT_SSIZE:
            value << iter->second.val.v_ssize;
            break;
        case DT_FLOAT:
            value << iter->second.val.v_float;
            break;
        case DT_CONFIGFILE:
        default:
            // ignore
            ;
        }

        std::stringstream key;
        key << "ep_" << iter->first;
        std::string k = key.str();
        add_casted_stat(k.c_str(), value.str().data(), add_stat, c);
    }
}

/**
 * Internal container of an engine parameter.
 */
class ConfigItem: public config_item {
public:
    ConfigItem(const char *theKey, config_datatype theDatatype) :
                                                                holder(NULL) {
        key = theKey;
        datatype = theDatatype;
        value.dt_string = &holder;
    }

private:
    char *holder;
};

bool Configuration::parseConfiguration(const char *str,
                                       SERVER_HANDLE_V1* sapi) {
    std::vector<ConfigItem *> config;

    std::map<std::string, value_t>::const_iterator iter;
    for (iter = attributes.begin(); iter != attributes.end(); ++iter) {
        config.push_back(new ConfigItem(iter->first.c_str(),
                                        iter->second.datatype));
    }

    // we don't have a good support for alias yet...
    config.push_back(new ConfigItem("cache_size", DT_SIZE));

    // And add support for config files...
    config.push_back(new ConfigItem("config_file", DT_CONFIGFILE));

    const int nelem = config.size();
    std::vector<config_item> items(nelem + 1);
    for (int ii = 0; ii < nelem; ++ii) {
        items[ii].key = config[ii]->key;
        items[ii].datatype = config[ii]->datatype;
        items[ii].value.dt_string = config[ii]->value.dt_string;
    }

    bool ret = sapi->core->parse_config(str, items.data(), stderr) == 0;
    for (int ii = 0; ii < nelem; ++ii) {
        if (items[ii].found) {
            if (ret) {
                switch (items[ii].datatype) {
                case DT_STRING:
                    setParameter(items[ii].key, *(items[ii].value.dt_string));
                    break;
                case DT_SIZE:
                    setParameter(items[ii].key, *items[ii].value.dt_size);
                    break;
                case DT_SSIZE:
                    setParameter(items[ii].key,
                                 (ssize_t)*items[ii].value.dt_ssize);
                    break;
                case DT_BOOL:
                    setParameter(items[ii].key, *items[ii].value.dt_bool);
                    break;
                case DT_FLOAT:
                    setParameter(items[ii].key, *items[ii].value.dt_float);
                    break;
                default:
                    abort();
                }
            }

            if (items[ii].datatype == DT_STRING) {
                free(*items[ii].value.dt_string);
            }
        }
    }

    std::vector<ConfigItem *>::iterator ii;
    for (ii = config.begin(); ii != config.end(); ++ii) {
        delete *ii;
    }

    return ret;
}

Configuration::~Configuration() {
    std::map<std::string, value_t>::iterator iter;
    for (iter = attributes.begin(); iter != attributes.end(); ++iter) {
        std::vector<ValueChangedListener*>::iterator ii;
        for (ii = iter->second.changeListener.begin();
             ii != iter->second.changeListener.end(); ++ii) {
            delete *ii;
        }

        delete iter->second.validator;
        if (iter->second.datatype == DT_STRING) {
            free((void*)iter->second.val.v_string);
        }
    }
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Kaveh Vahedipour
////////////////////////////////////////////////////////////////////////////////

#include "RestAgencyHandler.h"

#include <velocypack/Builder.h>
#include <velocypack/velocypack-aliases.h>

#include "Agency/Agent.h"
#include "Basics/StaticStrings.h"
#include "Logger/Logger.h"
#include "Rest/HttpRequest.h"
#include "Rest/Version.h"

#include <thread>

using namespace arangodb;

using namespace arangodb::basics;
using namespace arangodb::rest;
using namespace arangodb::consensus;

////////////////////////////////////////////////////////////////////////////////
/// @brief ArangoDB server
////////////////////////////////////////////////////////////////////////////////

RestAgencyHandler::RestAgencyHandler(GeneralRequest* request,
                                     GeneralResponse* response, Agent* agent)
    : RestBaseHandler(request, response), _agent(agent) {}

bool RestAgencyHandler::isDirect() const { return false; }

inline RestHandler::status RestAgencyHandler::reportErrorEmptyRequest() {
  LOG_TOPIC(WARN, Logger::AGENCY)
      << "Empty request to public agency interface.";
  generateError(rest::ResponseCode::NOT_FOUND, 404);
  return status::DONE;
}

inline RestHandler::status RestAgencyHandler::reportTooManySuffices() {
  LOG_TOPIC(WARN, Logger::AGENCY)
      << "Too many suffixes. Agency public interface takes one path.";
  generateError(rest::ResponseCode::NOT_FOUND, 404);
  return status::DONE;
}

inline RestHandler::status RestAgencyHandler::reportUnknownMethod() {
  LOG_TOPIC(WARN, Logger::AGENCY) << "Public REST interface has no method "
                                  << _request->suffix()[0];
  generateError(rest::ResponseCode::NOT_FOUND, 405);
  return status::DONE;
}

void RestAgencyHandler::redirectRequest(std::string const& leaderId) {
  try {
    std::string url =
      Endpoint::uriForm(_agent->config().poolAt(leaderId)) +
        _request->requestPath();
    _response->setHeaderNC(StaticStrings::Location, url);
  } catch (std::exception const& e) {
    LOG_TOPIC(WARN, Logger::AGENCY) << e.what() << " " << __FILE__ << __LINE__;
    generateError(rest::ResponseCode::SERVER_ERROR,
                  TRI_ERROR_INTERNAL, e.what());
  }
}

RestHandler::status RestAgencyHandler::handleStores() {
  if (_request->requestType() == rest::RequestType::GET) {
    Builder body;
    body.openObject();
    body.add("spearhead", VPackValue(VPackValueType::Array));
    _agent->spearhead().dumpToBuilder(body);
    body.close();
    body.add("read_db", VPackValue(VPackValueType::Array));
    _agent->readDB().dumpToBuilder(body);
    body.close();
    body.close();
    generateResult(rest::ResponseCode::OK, body.slice());
  } else {
    generateError(rest::ResponseCode::BAD, 400);
  }
  return status::DONE;
}

RestHandler::status RestAgencyHandler::handleWrite() {
  arangodb::velocypack::Options options;  // TODO: User not wait.
  if (_request->requestType() == rest::RequestType::POST) {
    query_t query;

    try {
      query = _request->toVelocyPackBuilderPtr(&options);
    } catch (std::exception const& e) {
      LOG_TOPIC(ERR, Logger::AGENCY) << e.what() << " " << __FILE__ << __LINE__;
      Builder body;
      body.openObject();
      body.add("message", VPackValue(e.what()));
      body.close();
      generateResult(rest::ResponseCode::BAD, body.slice());
      return status::DONE;
    }

    if (!query->slice().isArray()) {
      Builder body;
      body.openObject();
      body.add("message",
               VPackValue("Excpecting array of arrays as body for writes"));
      body.close();
      generateResult(rest::ResponseCode::BAD, body.slice());
      return status::DONE;
    }

    if (query->slice().length() == 0) {
      Builder body;
      body.openObject();
      body.add("message", VPackValue("Empty request."));
      body.close();
      generateResult(rest::ResponseCode::BAD, body.slice());
      return status::DONE;
    }

    auto s = std::chrono::system_clock::now();  // Leadership established?
    std::chrono::duration<double> timeout(_agent->config().minPing());
    while (_agent->size() > 1 && _agent->leaderID() == NO_LEADER) {
      if ((std::chrono::system_clock::now() - s) > timeout) {
        Builder body;
        body.openObject();
        body.add("message", VPackValue("No leader"));
        body.close();
        generateResult(rest::ResponseCode::SERVICE_UNAVAILABLE,
                       body.slice());
        LOG_TOPIC(ERR, Logger::AGENCY) << "We don't know who the leader is";
        return status::DONE;
      }
      std::this_thread::sleep_for(duration_t(100));
    }

    write_ret_t ret;

    try {
      ret = _agent->write(query);
    } catch (std::exception const& e) {
      LOG_TOPIC(DEBUG, Logger::AGENCY) << "Malformed write query " << query;
      Builder body;
      body.openObject();
      body.add("message",
               VPackValue(std::string("Malformed write query") + e.what()));
      body.close();
      generateResult(rest::ResponseCode::BAD, body.slice());
      return status::DONE;
    }

    if (ret.accepted) {  // We're leading and handling the request
      bool found;
      std::string call_mode = _request->header("x-arangodb-agency-mode", found);
      if (!found) {
        call_mode = "waitForCommitted";
      }

      size_t errors = 0;
      Builder body;
      body.openObject();

      if (call_mode != "noWait") {
        // Note success/error
        body.add("results", VPackValue(VPackValueType::Array));
        for (auto const& index : ret.indices) {
          body.add(VPackValue(index));
          if (index == 0) {
            errors++;
          }
        }
        body.close();

        // Wait for commit of highest except if it is 0?
        if (!ret.indices.empty() && call_mode == "waitForCommitted") {
          arangodb::consensus::index_t max_index = 0;
          try {
            max_index =
                *std::max_element(ret.indices.begin(), ret.indices.end());
          } catch (std::exception const& e) {
            LOG_TOPIC(WARN, Logger::AGENCY) << e.what() << " " << __FILE__
                                            << __LINE__;
          }

          if (max_index > 0) {
            _agent->waitFor(max_index);
          }
        }
      }

      body.close();

      if (errors > 0) {  // Some/all requests failed
        generateResult(rest::ResponseCode::PRECONDITION_FAILED,
                       body.slice());
      } else {  // All good
        generateResult(rest::ResponseCode::OK, body.slice());
      }
    } else {  // Redirect to leader
      if (_agent->leaderID() == NO_LEADER) {
        Builder body;
        body.openObject();
        body.add("message", VPackValue("No leader"));
        body.close();
        generateResult(rest::ResponseCode::SERVICE_UNAVAILABLE,
                       body.slice());
        LOG_TOPIC(ERR, Logger::AGENCY) << "We don't know who the leader is";
        return status::DONE;
      } else {
        redirectRequest(ret.redirect);
      }
    }
  } else {  // Unknown method
    generateError(rest::ResponseCode::METHOD_NOT_ALLOWED, 405);
  }
  return status::DONE;
}

inline RestHandler::status RestAgencyHandler::handleRead() {
  arangodb::velocypack::Options options;
  if (_request->requestType() == rest::RequestType::POST) {
    query_t query;
    try {
      query = _request->toVelocyPackBuilderPtr(&options);
    } catch (std::exception const& e) {
      LOG_TOPIC(WARN, Logger::AGENCY) << e.what() << " " << __FILE__ << ":" << __LINE__;
      generateError(rest::ResponseCode::BAD, 400);
      return status::DONE;
    }

    auto s = std::chrono::system_clock::now();  // Leadership established?
    std::chrono::duration<double> timeout(_agent->config().minPing());
    while (_agent->size() > 1 && _agent->leaderID() == NO_LEADER) {
      if ((std::chrono::system_clock::now() - s) > timeout) {
        Builder body;
        body.openObject();
        body.add("message", VPackValue("No leader"));
        body.close();
        generateResult(rest::ResponseCode::SERVICE_UNAVAILABLE,
                       body.slice());
        LOG_TOPIC(ERR, Logger::AGENCY) << "We don't know who the leader is";
        return status::DONE;
      }
      std::this_thread::sleep_for(duration_t(100));
    }

    read_ret_t ret = _agent->read(query);

    if (ret.accepted) {  // I am leading
      if (ret.success.size() == 1 && !ret.success.at(0)) {
        generateResult(rest::ResponseCode::I_AM_A_TEAPOT,
                       ret.result->slice());
      } else {
        generateResult(rest::ResponseCode::OK, ret.result->slice());
      }
    } else {  // Redirect to leader
      if (_agent->leaderID() == NO_LEADER) {
        Builder body;
        body.openObject();
        body.add("message", VPackValue("No leader"));
        body.close();
        generateResult(rest::ResponseCode::SERVICE_UNAVAILABLE,
                       body.slice());
        LOG_TOPIC(ERR, Logger::AGENCY) << "We don't know who the leader is";
        return status::DONE;

      } else {
        redirectRequest(ret.redirect);
      }
      return status::DONE;
    }
  } else {
    generateError(rest::ResponseCode::METHOD_NOT_ALLOWED, 405);
    return status::DONE;
  }
  return status::DONE;
}

RestHandler::status RestAgencyHandler::handleConfig() {
  Builder body;
  body.add(VPackValue(VPackValueType::Object));
  body.add("term", Value(_agent->term()));
  body.add("leaderId", Value(_agent->leaderID()));
  body.add("lastCommitted", Value(_agent->lastCommitted()));
  body.add("configuration", _agent->config().toBuilder()->slice());
  body.close();
  generateResult(rest::ResponseCode::OK, body.slice());
  return status::DONE;
}

RestHandler::status RestAgencyHandler::handleState() {
  Builder body;
  body.add(VPackValue(VPackValueType::Array));
  for (auto const& i : _agent->state().get()) {
    body.add(VPackValue(VPackValueType::Object));
    body.add("index", VPackValue(i.index));
    body.add("term", VPackValue(i.term));
    body.add("query", VPackSlice(i.entry->data()));
    body.close();
  }
  body.close();
  generateResult(rest::ResponseCode::OK, body.slice());
  return status::DONE;
}

inline RestHandler::status RestAgencyHandler::reportMethodNotAllowed() {
  generateError(rest::ResponseCode::METHOD_NOT_ALLOWED, 405);
  return status::DONE;
}

RestHandler::status RestAgencyHandler::execute() {
  try {
    if (_request->suffix().size() == 0) {  // Empty request
      return reportErrorEmptyRequest();
    } else if (_request->suffix().size() > 1) {  // path size >= 2
      return reportTooManySuffices();
    } else {
      if (_request->suffix()[0] == "write") {
        return handleWrite();
      } else if (_request->suffix()[0] == "read") {
        return handleRead();
      } else if (_request->suffix()[0] == "config") {
        if (_request->requestType() != rest::RequestType::GET) {
          return reportMethodNotAllowed();
        }
        return handleConfig();
      } else if (_request->suffix()[0] == "state") {
        if (_request->requestType() != rest::RequestType::GET) {
          return reportMethodNotAllowed();
        }
        return handleState();
      } else if (_request->suffix()[0] == "stores") {
        return handleStores();
      } else {
        return reportUnknownMethod();
      }
    }
  } catch (...) {
    // Ignore this error
  }
  return status::DONE;
}
<commit_msg>properly set response code on redirect<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Kaveh Vahedipour
////////////////////////////////////////////////////////////////////////////////

#include "RestAgencyHandler.h"

#include <velocypack/Builder.h>
#include <velocypack/velocypack-aliases.h>

#include "Agency/Agent.h"
#include "Basics/StaticStrings.h"
#include "Logger/Logger.h"
#include "Rest/HttpRequest.h"
#include "Rest/Version.h"

#include <thread>

using namespace arangodb;

using namespace arangodb::basics;
using namespace arangodb::rest;
using namespace arangodb::consensus;

////////////////////////////////////////////////////////////////////////////////
/// @brief ArangoDB server
////////////////////////////////////////////////////////////////////////////////

RestAgencyHandler::RestAgencyHandler(GeneralRequest* request,
                                     GeneralResponse* response, Agent* agent)
    : RestBaseHandler(request, response), _agent(agent) {}

bool RestAgencyHandler::isDirect() const { return false; }

inline RestHandler::status RestAgencyHandler::reportErrorEmptyRequest() {
  LOG_TOPIC(WARN, Logger::AGENCY)
      << "Empty request to public agency interface.";
  generateError(rest::ResponseCode::NOT_FOUND, 404);
  return status::DONE;
}

inline RestHandler::status RestAgencyHandler::reportTooManySuffices() {
  LOG_TOPIC(WARN, Logger::AGENCY)
      << "Too many suffixes. Agency public interface takes one path.";
  generateError(rest::ResponseCode::NOT_FOUND, 404);
  return status::DONE;
}

inline RestHandler::status RestAgencyHandler::reportUnknownMethod() {
  LOG_TOPIC(WARN, Logger::AGENCY) << "Public REST interface has no method "
                                  << _request->suffix()[0];
  generateError(rest::ResponseCode::NOT_FOUND, 405);
  return status::DONE;
}

void RestAgencyHandler::redirectRequest(std::string const& leaderId) {
  try {
    std::string url =
      Endpoint::uriForm(_agent->config().poolAt(leaderId)) +
        _request->requestPath();
    _response->setResponseCode(rest::ResponseCode::TEMPORARY_REDIRECT);
    _response->setHeaderNC(StaticStrings::Location, url);
  } catch (std::exception const& e) {
    LOG_TOPIC(WARN, Logger::AGENCY) << e.what() << " " << __FILE__ << __LINE__;
    generateError(rest::ResponseCode::SERVER_ERROR,
                  TRI_ERROR_INTERNAL, e.what());
  }
}

RestHandler::status RestAgencyHandler::handleStores() {
  if (_request->requestType() == rest::RequestType::GET) {
    Builder body;
    body.openObject();
    body.add("spearhead", VPackValue(VPackValueType::Array));
    _agent->spearhead().dumpToBuilder(body);
    body.close();
    body.add("read_db", VPackValue(VPackValueType::Array));
    _agent->readDB().dumpToBuilder(body);
    body.close();
    body.close();
    generateResult(rest::ResponseCode::OK, body.slice());
  } else {
    generateError(rest::ResponseCode::BAD, 400);
  }
  return status::DONE;
}

RestHandler::status RestAgencyHandler::handleWrite() {
  arangodb::velocypack::Options options;  // TODO: User not wait.
  if (_request->requestType() == rest::RequestType::POST) {
    query_t query;

    try {
      query = _request->toVelocyPackBuilderPtr(&options);
    } catch (std::exception const& e) {
      LOG_TOPIC(ERR, Logger::AGENCY) << e.what() << " " << __FILE__ << __LINE__;
      Builder body;
      body.openObject();
      body.add("message", VPackValue(e.what()));
      body.close();
      generateResult(rest::ResponseCode::BAD, body.slice());
      return status::DONE;
    }

    if (!query->slice().isArray()) {
      Builder body;
      body.openObject();
      body.add("message",
               VPackValue("Excpecting array of arrays as body for writes"));
      body.close();
      generateResult(rest::ResponseCode::BAD, body.slice());
      return status::DONE;
    }

    if (query->slice().length() == 0) {
      Builder body;
      body.openObject();
      body.add("message", VPackValue("Empty request."));
      body.close();
      generateResult(rest::ResponseCode::BAD, body.slice());
      return status::DONE;
    }

    auto s = std::chrono::system_clock::now();  // Leadership established?
    std::chrono::duration<double> timeout(_agent->config().minPing());
    while (_agent->size() > 1 && _agent->leaderID() == NO_LEADER) {
      if ((std::chrono::system_clock::now() - s) > timeout) {
        Builder body;
        body.openObject();
        body.add("message", VPackValue("No leader"));
        body.close();
        generateResult(rest::ResponseCode::SERVICE_UNAVAILABLE,
                       body.slice());
        LOG_TOPIC(ERR, Logger::AGENCY) << "We don't know who the leader is";
        return status::DONE;
      }
      std::this_thread::sleep_for(duration_t(100));
    }

    write_ret_t ret;

    try {
      ret = _agent->write(query);
    } catch (std::exception const& e) {
      LOG_TOPIC(DEBUG, Logger::AGENCY) << "Malformed write query " << query;
      Builder body;
      body.openObject();
      body.add("message",
               VPackValue(std::string("Malformed write query") + e.what()));
      body.close();
      generateResult(rest::ResponseCode::BAD, body.slice());
      return status::DONE;
    }

    if (ret.accepted) {  // We're leading and handling the request
      bool found;
      std::string call_mode = _request->header("x-arangodb-agency-mode", found);
      if (!found) {
        call_mode = "waitForCommitted";
      }

      size_t errors = 0;
      Builder body;
      body.openObject();

      if (call_mode != "noWait") {
        // Note success/error
        body.add("results", VPackValue(VPackValueType::Array));
        for (auto const& index : ret.indices) {
          body.add(VPackValue(index));
          if (index == 0) {
            errors++;
          }
        }
        body.close();

        // Wait for commit of highest except if it is 0?
        if (!ret.indices.empty() && call_mode == "waitForCommitted") {
          arangodb::consensus::index_t max_index = 0;
          try {
            max_index =
                *std::max_element(ret.indices.begin(), ret.indices.end());
          } catch (std::exception const& e) {
            LOG_TOPIC(WARN, Logger::AGENCY) << e.what() << " " << __FILE__
                                            << __LINE__;
          }

          if (max_index > 0) {
            _agent->waitFor(max_index);
          }
        }
      }

      body.close();

      if (errors > 0) {  // Some/all requests failed
        generateResult(rest::ResponseCode::PRECONDITION_FAILED,
                       body.slice());
      } else {  // All good
        generateResult(rest::ResponseCode::OK, body.slice());
      }
    } else {  // Redirect to leader
      if (_agent->leaderID() == NO_LEADER) {
        Builder body;
        body.openObject();
        body.add("message", VPackValue("No leader"));
        body.close();
        generateResult(rest::ResponseCode::SERVICE_UNAVAILABLE,
                       body.slice());
        LOG_TOPIC(ERR, Logger::AGENCY) << "We don't know who the leader is";
        return status::DONE;
      } else {
        redirectRequest(ret.redirect);
      }
    }
  } else {  // Unknown method
    generateError(rest::ResponseCode::METHOD_NOT_ALLOWED, 405);
  }
  return status::DONE;
}

inline RestHandler::status RestAgencyHandler::handleRead() {
  arangodb::velocypack::Options options;
  if (_request->requestType() == rest::RequestType::POST) {
    query_t query;
    try {
      query = _request->toVelocyPackBuilderPtr(&options);
    } catch (std::exception const& e) {
      LOG_TOPIC(WARN, Logger::AGENCY) << e.what() << " " << __FILE__ << ":" << __LINE__;
      generateError(rest::ResponseCode::BAD, 400);
      return status::DONE;
    }

    auto s = std::chrono::system_clock::now();  // Leadership established?
    std::chrono::duration<double> timeout(_agent->config().minPing());
    while (_agent->size() > 1 && _agent->leaderID() == NO_LEADER) {
      if ((std::chrono::system_clock::now() - s) > timeout) {
        Builder body;
        body.openObject();
        body.add("message", VPackValue("No leader"));
        body.close();
        generateResult(rest::ResponseCode::SERVICE_UNAVAILABLE,
                       body.slice());
        LOG_TOPIC(ERR, Logger::AGENCY) << "We don't know who the leader is";
        return status::DONE;
      }
      std::this_thread::sleep_for(duration_t(100));
    }

    read_ret_t ret = _agent->read(query);

    if (ret.accepted) {  // I am leading
      if (ret.success.size() == 1 && !ret.success.at(0)) {
        generateResult(rest::ResponseCode::I_AM_A_TEAPOT,
                       ret.result->slice());
      } else {
        generateResult(rest::ResponseCode::OK, ret.result->slice());
      }
    } else {  // Redirect to leader
      if (_agent->leaderID() == NO_LEADER) {
        Builder body;
        body.openObject();
        body.add("message", VPackValue("No leader"));
        body.close();
        generateResult(rest::ResponseCode::SERVICE_UNAVAILABLE,
                       body.slice());
        LOG_TOPIC(ERR, Logger::AGENCY) << "We don't know who the leader is";
        return status::DONE;

      } else {
        redirectRequest(ret.redirect);
      }
      return status::DONE;
    }
  } else {
    generateError(rest::ResponseCode::METHOD_NOT_ALLOWED, 405);
    return status::DONE;
  }
  return status::DONE;
}

RestHandler::status RestAgencyHandler::handleConfig() {
  Builder body;
  body.add(VPackValue(VPackValueType::Object));
  body.add("term", Value(_agent->term()));
  body.add("leaderId", Value(_agent->leaderID()));
  body.add("lastCommitted", Value(_agent->lastCommitted()));
  body.add("configuration", _agent->config().toBuilder()->slice());
  body.close();
  generateResult(rest::ResponseCode::OK, body.slice());
  return status::DONE;
}

RestHandler::status RestAgencyHandler::handleState() {
  Builder body;
  body.add(VPackValue(VPackValueType::Array));
  for (auto const& i : _agent->state().get()) {
    body.add(VPackValue(VPackValueType::Object));
    body.add("index", VPackValue(i.index));
    body.add("term", VPackValue(i.term));
    body.add("query", VPackSlice(i.entry->data()));
    body.close();
  }
  body.close();
  generateResult(rest::ResponseCode::OK, body.slice());
  return status::DONE;
}

inline RestHandler::status RestAgencyHandler::reportMethodNotAllowed() {
  generateError(rest::ResponseCode::METHOD_NOT_ALLOWED, 405);
  return status::DONE;
}

RestHandler::status RestAgencyHandler::execute() {
  try {
    if (_request->suffix().size() == 0) {  // Empty request
      return reportErrorEmptyRequest();
    } else if (_request->suffix().size() > 1) {  // path size >= 2
      return reportTooManySuffices();
    } else {
      if (_request->suffix()[0] == "write") {
        return handleWrite();
      } else if (_request->suffix()[0] == "read") {
        return handleRead();
      } else if (_request->suffix()[0] == "config") {
        if (_request->requestType() != rest::RequestType::GET) {
          return reportMethodNotAllowed();
        }
        return handleConfig();
      } else if (_request->suffix()[0] == "state") {
        if (_request->requestType() != rest::RequestType::GET) {
          return reportMethodNotAllowed();
        }
        return handleState();
      } else if (_request->suffix()[0] == "stores") {
        return handleStores();
      } else {
        return reportUnknownMethod();
      }
    }
  } catch (...) {
    // Ignore this error
  }
  return status::DONE;
}
<|endoftext|>


<commit_before>#ifndef Exc_Ruby___cpp__st__hpp_
#define Exc_Ruby___cpp__st__hpp_

/*! \file
 * \brief Hacks to allow functions in st.h to be called from C++
 * programs.
 */

#include "ruby.hpp"

// Ruby doesn't put extern "C" around st.h

extern "C"
{
#include "st.h"
}

// Older versions of Ruby don't have proper signatures for the st_
// functions

#if RICE__RUBY_VERSION_CODE < 180

typedef char * st_data_t;

namespace Exc_Ruby
{

namespace detail
{

extern "C" typedef int (*St_Insert_Signature)(
    st_table * table,
    st_data_t key,
    st_data_t * value);
extern "C" typedef int (*St_Lookup_Signature)(
    st_table * table,
    st_data_t key,
    st_data_t * value);
extern "C" typedef st_table* (*St_Init_Table_Signature)(
    struct st_hash_type * type);

#define st_insert(table, key, value) \
  ((::Exc_Ruby::detail::St_Insert_Signature)(st_insert))(table, key, value)
#define st_lookup(table, key, value) \
  ((::Exc_Ruby::detail::St_Lookup_Signature)(st_lookup))(table, key, value)
#define st_init_table(type) \
  ((::Exc_Ruby::detail::St_Init_Table_Signature)(st_init_table))(type)

} // namespace detail

} // namespace Exc_Ruby

#endif // RICE__RUBY_VERSION_CODE < 180

#endif // Exc_Ruby___cpp__st__hpp_

<commit_msg>Include ruby/st.h on YARV.<commit_after>#ifndef Exc_Ruby___cpp__st__hpp_
#define Exc_Ruby___cpp__st__hpp_

/*! \file
 * \brief Hacks to allow functions in st.h to be called from C++
 * programs.
 */

#include "ruby.hpp"

// Ruby doesn't put extern "C" around st.h

extern "C"
{
#ifdef RUBY_VM
#include "ruby/st.h"
#else
#include "st.h"
#endif
}

// Older versions of Ruby don't have proper signatures for the st_
// functions

#if RICE__RUBY_VERSION_CODE < 180

typedef char * st_data_t;

namespace Exc_Ruby
{

namespace detail
{

extern "C" typedef int (*St_Insert_Signature)(
    st_table * table,
    st_data_t key,
    st_data_t * value);
extern "C" typedef int (*St_Lookup_Signature)(
    st_table * table,
    st_data_t key,
    st_data_t * value);
extern "C" typedef st_table* (*St_Init_Table_Signature)(
    struct st_hash_type * type);

#define st_insert(table, key, value) \
  ((::Exc_Ruby::detail::St_Insert_Signature)(st_insert))(table, key, value)
#define st_lookup(table, key, value) \
  ((::Exc_Ruby::detail::St_Lookup_Signature)(st_lookup))(table, key, value)
#define st_init_table(type) \
  ((::Exc_Ruby::detail::St_Init_Table_Signature)(st_init_table))(type)

} // namespace detail

} // namespace Exc_Ruby

#endif // RICE__RUBY_VERSION_CODE < 180

#endif // Exc_Ruby___cpp__st__hpp_

<|endoftext|>


<commit_before>#include <string>

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

#include "rpc/server.h"
#include "rpc/client.h"
#include "log_service_impl.h"

using namespace std;
using namespace rpc;
using namespace rlog;

Mutex g_stop_mutex;
CondVar g_stop_condvar;
bool g_stop_flag = false;

static void signal_handler(int sig) {
    Log_info("caught signal %d, stopping server now", sig);
    g_stop_flag = true;
    g_stop_condvar.signal();
}

int main(int argc, char* argv[]) {
    string bind_addr = "0.0.0.0:8848";
    printf("usage: %s [bind_addr=%s]\n", argv[0], bind_addr.c_str());
    if (argc >= 2) {
        bind_addr = argv[1];
        if (bind_addr.find(":") == string::npos) {
            bind_addr = "0.0.0.0:" + bind_addr;
        }
    }

    Log::set_level(Log::INFO);

    RLogService* log_service = new RLogServiceImpl;
    Server* server = new Server;
    server->reg(log_service);

    int ret;
    if ((ret = server->start(bind_addr.c_str())) == 0) {
        signal(SIGPIPE, SIG_IGN);
        signal(SIGHUP, SIG_IGN);
        signal(SIGCHLD, SIG_IGN);

        signal(SIGALRM, signal_handler);
        signal(SIGINT, signal_handler);
        signal(SIGQUIT, signal_handler);
        signal(SIGTERM, signal_handler);

        g_stop_mutex.lock();
        while (g_stop_flag == false) {
            g_stop_condvar.wait(g_stop_mutex);
        }
        g_stop_mutex.unlock();

        delete server;
        delete log_service;
    }

    return ret;
}
<commit_msg>no global static condvar and mutex<commit_after>#include <string>

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

#include "rpc/server.h"
#include "rpc/client.h"
#include "log_service_impl.h"

using namespace std;
using namespace rpc;
using namespace rlog;

pthread_mutex_t g_stop_mutex;
pthread_cond_t g_stop_cond;
bool g_stop_flag = false;

static void signal_handler(int sig) {
    Log_info("caught signal %d, stopping server now", sig);
    g_stop_flag = true;
    Pthread_mutex_lock(&g_stop_mutex);
    Pthread_cond_signal(&g_stop_cond);
    Pthread_mutex_unlock(&g_stop_mutex);
}

int main(int argc, char* argv[]) {
    string bind_addr = "0.0.0.0:8848";
    printf("usage: %s [bind_addr=%s]\n", argv[0], bind_addr.c_str());
    if (argc >= 2) {
        bind_addr = argv[1];
        if (bind_addr.find(":") == string::npos) {
            bind_addr = "0.0.0.0:" + bind_addr;
        }
    }

    Pthread_mutex_init(&g_stop_mutex, nullptr);
    Pthread_cond_init(&g_stop_cond, nullptr);

    Log::set_level(Log::INFO);

    RLogService* log_service = new RLogServiceImpl;
    Server* server = new Server;
    server->reg(log_service);

    int ret;
    if ((ret = server->start(bind_addr.c_str())) == 0) {
        signal(SIGPIPE, SIG_IGN);
        signal(SIGHUP, SIG_IGN);
        signal(SIGCHLD, SIG_IGN);

        signal(SIGALRM, signal_handler);
        signal(SIGINT, signal_handler);
        signal(SIGQUIT, signal_handler);
        signal(SIGTERM, signal_handler);

        Pthread_mutex_lock(&g_stop_mutex);
        while (g_stop_flag == false) {
            Pthread_cond_wait(&g_stop_cond, &g_stop_mutex);
        }
        Pthread_mutex_unlock(&g_stop_mutex);

        delete server;
        delete log_service;
    }

    Pthread_mutex_destroy(&g_stop_mutex);
    Pthread_cond_destroy(&g_stop_cond);

    return ret;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: backendfactory.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: cyrillem $ $Date: 2002-06-13 16:45:55 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include "backendfactory.hxx"

#ifndef CONFIGMGR_BOOTSTRAPCONTEXT_HXX_
#include "bootstrapcontext.hxx"
#endif
#ifndef CONFIGMGR_BOOTSTRAP_HXX_
#include "bootstrap.hxx"
#endif
#ifndef CONFIGMGR_BACKEND_BACKENDACCESS_HXX_
#include "backendaccess.hxx"
#endif
#ifndef CONFIGMGR_BACKENDWRAP_HXX
#include "backendwrap.hxx"
#endif

#include <drafts/com/sun/star/configuration/backend/XBackend.hpp>
#include <drafts/com/sun/star/configuration/backend/XSingleBackend.hpp>

namespace configmgr
{
// -------------------------------------------------------------------------
    namespace backend
    {
// -------------------------------------------------------------------------
        namespace uno = ::com::sun::star::uno;
        namespace lang = ::com::sun::star::lang;
        namespace backenduno = drafts::com::sun::star::configuration::backend;
// -------------------------------------------------------------------------
const sal_Char k_DefaultBackendWrapper[] = "com.sun.star.comp.configuration.backend.SingleBackendAdapter";
const sal_Char k_DefaultBackendService[] = "com.sun.star.configuration.backend.Backend";
// -------------------------------------------------------------------------
typedef uno::Sequence< uno::Any > UnoInitArgs;

static
UnoInitArgs createInitArgs(ConnectionSettings const & _aSettings)
{
    uno::Reference< uno::XCurrentContext > xBootstrapArgs = new BootstrapContext(_aSettings.getUnoSettings());
    uno::Sequence< uno::Any > aResult( 1 );
    aResult[0] <<= xBootstrapArgs;
    return aResult;
}
// -------------------------------------------------------------------------

typedef BackendFactory::CreationContext CreationContext;

static
inline
uno::Reference< uno::XInterface > createService(CreationContext const & _xCtx, UnoInitArgs const & _aInitArgs, OUString const & _aSvc)
{
    OSL_ASSERT(_xCtx.is());
    return _xCtx->createInstanceWithArguments( _aSvc, _aInitArgs);
}
// -------------------------------------------------------------------------

typedef uno::Reference< backenduno::XSingleBackend >    UnoSingleBackend;
typedef uno::Reference< backenduno::XBackend >          UnoBackend;

static
UnoBackend wrapSingleBackend(ConnectionSettings const & _aSettings, CreationContext const & _xCtx, UnoInitArgs const & _aInitArgs, UnoSingleBackend const & _xWrappedBackend)
{
    OUString aWrapperSvc = _aSettings.hasUnoBackendWrapper() ?
                                _aSettings.getUnoBackendWrapper() :
                                OUString::createFromAscii(k_DefaultBackendWrapper);

    OSL_ENSURE (aWrapperSvc.getLength(), "ERROR: No wrapper service for wrapped configuration");
    OSL_ENSURE (_xWrappedBackend.is(), "ERROR: No backend to wrap for wrapped configuration");

    sal_Int32 const nBaseArgsCount = _aInitArgs.getLength();
    UnoInitArgs aExtendedArgs( _aInitArgs );
    aExtendedArgs.realloc( nBaseArgsCount + 1 );
    aExtendedArgs[nBaseArgsCount] <<= _xWrappedBackend;

    return UnoBackend::query( createService(_xCtx,aExtendedArgs,aWrapperSvc) );
}
// -------------------------------------------------------------------------

static
UnoBackend createOfflineBackend(ConnectionSettings const & _aSettings, CreationContext const & _xCtx, UnoInitArgs const & _aInitArgs)
{
    UnoBackend xResult;
    if (_aSettings.hasOfflineSetting() && _aSettings.getOfflineSetting())
    {
        OSL_ENSURE(_aSettings.hasUnoBackendWrapper(), "ERROR: No offline service for offline configuration");
        if (_aSettings.hasUnoBackendWrapper())
        {
            xResult.set( createService(_xCtx,_aInitArgs,_aSettings.getUnoBackendWrapper()), uno::UNO_QUERY );

            OSL_ENSURE(xResult.is(), "ERROR: Cannot create offline service");
        }
    }
    return xResult;
}
// -------------------------------------------------------------------------

static
UnoBackend createOnlineBackend(ConnectionSettings const & _aSettings, CreationContext const & _xCtx, UnoInitArgs const & _aInitArgs)
{
    OSL_ENSURE( _aSettings.isUnoBackend(), "ERROR - BackendFactory: For legacy backends use createSessionBackend()");

    UnoBackend xResult;

    if (_aSettings.hasUnoBackendService())
    {
        uno::Reference< uno::XInterface > xRealBackend = createService(_xCtx,_aInitArgs,_aSettings.getUnoBackendService());
        xResult.set(xRealBackend, uno::UNO_QUERY);

        if (!xResult.is())
        {
            UnoSingleBackend xSingleRealBackend( xRealBackend, uno::UNO_QUERY);
            if (xSingleRealBackend.is())
                xResult = wrapSingleBackend(_aSettings,_xCtx,_aInitArgs,xSingleRealBackend);

            else
                OSL_ENSURE( !xRealBackend.is(), "Configuration Backend implements no known interface" );
        }
    }
    else
    {
        OUString aBackendFallback( RTL_CONSTASCII_USTRINGPARAM(k_DefaultBackendService) );
        xResult.set( createService(_xCtx,_aInitArgs,aBackendFallback), uno::UNO_QUERY );
    }
    return xResult;
}
// -------------------------------------------------------------------------

static
UnoBackend createUnoBackend(ConnectionSettings const & _aSettings, CreationContext const & _xCtx)
{
    UnoInitArgs aArguments = createInitArgs(_aSettings);

    UnoBackend xResult = createOfflineBackend(_aSettings,_xCtx,aArguments);

    // not offline or no offline implementation available
    if (!xResult.is())
        xResult = createOnlineBackend(_aSettings,_xCtx,aArguments);

    return xResult;
}
// -------------------------------------------------------------------------

rtl::Reference<IMergedDataProvider>
    BackendFactory::createBackend(ConnectionSettings const & _aSettings, CreationContext const & _xCtx)
{
    OSL_ENSURE( _aSettings.isUnoBackend(), "ERROR - BackendFactory: For legacy backends use createSessionBackend()");

    rtl::Reference< IMergedDataProvider > xBackend;

    UnoBackend xBackendService = createUnoBackend(_aSettings, _xCtx);

    if (xBackendService.is())
        xBackend = new BackendAccess(xBackendService, _xCtx);

    return xBackend;
}
// -------------------------------------------------------------------------

rtl::Reference<IMergedDataProvider>
    BackendFactory::createSessionBackend(IConfigSession * _pSession,
                                            TypeConverterRef const & _xTCV)
{
    rtl::Reference< IMergedDataProvider > xBackend;

    if (_pSession)
        xBackend = wrapSession(*_pSession,_xTCV);

    return xBackend;
}
// -------------------------------------------------------------------------

BackendFactory & BackendFactory::instance()
{
    static BackendFactory aStaticFactory;
    return aStaticFactory;
}

//-----------------------------------------------------------------------------
    } // namespace
//-----------------------------------------------------------------------------
} // namespace
<commit_msg>#102804# Changed sequence of building and wrapping a backend to allow fallback to offline mode<commit_after>/*************************************************************************
 *
 *  $RCSfile: backendfactory.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: jb $ $Date: 2002-09-02 17:23:43 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include "backendfactory.hxx"

#ifndef CONFIGMGR_BOOTSTRAPCONTEXT_HXX_
#include "bootstrapcontext.hxx"
#endif
#ifndef CONFIGMGR_BOOTSTRAP_HXX_
#include "bootstrap.hxx"
#endif
#ifndef CONFIGMGR_BACKEND_BACKENDACCESS_HXX_
#include "backendaccess.hxx"
#endif
#ifndef CONFIGMGR_BACKENDWRAP_HXX
#include "backendwrap.hxx"
#endif

#ifndef _COM_SUN_STAR_CONFIGURATION_CANNOTLOADCONFIGURATIONEXCEPTION_HPP_
#include <com/sun/star/configuration/CannotLoadConfigurationException.hpp>
#endif

#include <drafts/com/sun/star/configuration/backend/XBackend.hpp>
#include <drafts/com/sun/star/configuration/backend/XSingleBackend.hpp>

namespace configmgr
{
// -------------------------------------------------------------------------
    namespace backend
    {
// -------------------------------------------------------------------------
        namespace uno = ::com::sun::star::uno;
        namespace lang = ::com::sun::star::lang;
        namespace backenduno = drafts::com::sun::star::configuration::backend;
// -------------------------------------------------------------------------
const sal_Char k_DefaultBackendWrapper[] = "com.sun.star.comp.configuration.backend.SingleBackendAdapter";
const sal_Char k_DefaultBackendService[] = "com.sun.star.comp.configuration.backend.LocalSingleBackend";
// -------------------------------------------------------------------------
typedef uno::Sequence< uno::Any > UnoInitArgs;

static
UnoInitArgs createInitArgs(ConnectionSettings const & _aSettings)
{
    uno::Reference< uno::XCurrentContext > xBootstrapArgs = new BootstrapContext(_aSettings.getUnoSettings());
    uno::Sequence< uno::Any > aResult( 1 );
    aResult[0] <<= xBootstrapArgs;
    return aResult;
}
// -------------------------------------------------------------------------

typedef BackendFactory::CreationContext CreationContext;

static
inline
uno::Reference< uno::XInterface > createService(CreationContext const & _xCtx, UnoInitArgs const & _aInitArgs, OUString const & _aSvc)
{
    OSL_ASSERT(_xCtx.is());
    return _xCtx->createInstanceWithArguments( _aSvc, _aInitArgs);
}
// -------------------------------------------------------------------------

typedef uno::Reference< backenduno::XSingleBackend >    UnoSingleBackend;
typedef uno::Reference< backenduno::XBackend >          UnoBackend;

static
UnoBackend wrapSingleBackend(ConnectionSettings const & _aSettings, CreationContext const & _xCtx, UnoInitArgs const & _aInitArgs, UnoSingleBackend const & _xWrappedBackend)
{
    OUString aWrapperSvc = _aSettings.hasUnoBackendWrapper() ?
                                _aSettings.getUnoBackendWrapper() :
                                OUString::createFromAscii(k_DefaultBackendWrapper);

    OSL_ENSURE (aWrapperSvc.getLength(), "ERROR: No wrapper service for wrapped configuration");
    OSL_ENSURE (_xWrappedBackend.is(), "ERROR: No backend to wrap for wrapped configuration");

    sal_Int32 const nBaseArgsCount = _aInitArgs.getLength();
    UnoInitArgs aExtendedArgs( _aInitArgs );
    aExtendedArgs.realloc( nBaseArgsCount + 1 );
    aExtendedArgs[nBaseArgsCount] <<= _xWrappedBackend;

    return UnoBackend::query( createService(_xCtx,aExtendedArgs,aWrapperSvc) );
}
// -------------------------------------------------------------------------

static
UnoBackend createOfflineBackend(ConnectionSettings const & _aSettings, CreationContext const & _xCtx, UnoInitArgs const & _aInitArgs)
{
    UnoBackend xResult;
    if ( _aSettings.hasUnoBackendWrapper() )
    {
        OUString const aWrapperSvc = _aSettings.getUnoBackendWrapper();

        xResult = UnoBackend::query( createService(_xCtx,_aInitArgs,aWrapperSvc) );
    }

    return xResult;
}
// -------------------------------------------------------------------------

static
sal_Bool createOfflineBackend_nothrow(UnoBackend & _rxResult, ConnectionSettings const & _aSettings, CreationContext const & _xCtx, UnoInitArgs const & _aInitArgs)
{
    try
    {
        _rxResult = createOfflineBackend(_aSettings,_xCtx,_aInitArgs);
        return _rxResult.is();
    }
    catch (uno::Exception &)
    {
        return false;
    }
}
// -------------------------------------------------------------------------

static
uno::Reference< uno::XInterface > createRealBackend(ConnectionSettings const & _aSettings, CreationContext const & _xCtx, UnoInitArgs const & _aInitArgs)
{
    OUString const aBackendServiceName = _aSettings.hasUnoBackendService() ?
                                        _aSettings.getUnoBackendService() :
                                        OUString::createFromAscii(k_DefaultBackendService);

    uno::Reference< uno::XInterface > xResult =
        createService(_xCtx,_aInitArgs,_aSettings.getUnoBackendService());

    return xResult;
}
// -------------------------------------------------------------------------

static
UnoBackend createOnlineBackend(ConnectionSettings const & _aSettings, CreationContext const & _xCtx, UnoInitArgs const & _aInitArgs)
{
    OSL_ENSURE( _aSettings.isUnoBackend(), "ERROR - BackendFactory: For legacy backends use createSessionBackend()");

    UnoBackend xResult;

    uno::Reference< uno::XInterface > xRealBackend = createRealBackend(_aSettings,_xCtx,_aInitArgs);

    if (_aSettings.hasUnoBackendWrapper())
    {
        // try wrapping a single backend
        UnoSingleBackend xSingleRealBackend( xRealBackend, uno::UNO_QUERY);
        if (xSingleRealBackend.is())
            xResult = wrapSingleBackend(_aSettings,_xCtx,_aInitArgs,xSingleRealBackend);

        // if we don't have one, try using it as unwrapped backend
        else
            xResult.set(xRealBackend, uno::UNO_QUERY);
    }
    else
    {
         // look for a direct implementation of XBackend
        xResult.set(xRealBackend, uno::UNO_QUERY);
        if (!xResult.is())
        {
            // try the default wrapper if we only have a single backend
            UnoSingleBackend xSingleRealBackend( xRealBackend, uno::UNO_QUERY);
            if (xSingleRealBackend.is())
                xResult = wrapSingleBackend(_aSettings,_xCtx,_aInitArgs,xSingleRealBackend);

            else
                OSL_ENSURE( !xRealBackend.is(), "Configuration Backend implements no known backendinterface" );
        }
    }

    return xResult;
}
// -------------------------------------------------------------------------

static
UnoBackend createUnoBackend(ConnectionSettings const & _aSettings, CreationContext const & _xCtx)
{
    UnoInitArgs aArguments = createInitArgs(_aSettings);

    UnoBackend xResult;
    try
    {
       xResult = createOnlineBackend(_aSettings,_xCtx,aArguments);
    }

    // for CannotLoadConfigurationException, try fallback to wrapper-only (offline) mode
    catch (com::sun::star::configuration::CannotLoadConfigurationException & )
    {
        if (!createOfflineBackend_nothrow(xResult,_aSettings,_xCtx,aArguments))
            throw;
    }

    if (!xResult.is())
        xResult = createOfflineBackend(_aSettings,_xCtx,aArguments);

    return xResult;
}
// -------------------------------------------------------------------------

rtl::Reference<IMergedDataProvider>
    BackendFactory::createBackend(ConnectionSettings const & _aSettings, CreationContext const & _xCtx)
{
    OSL_ENSURE( _aSettings.isUnoBackend(), "ERROR - BackendFactory: For legacy backends use createSessionBackend()");

    rtl::Reference< IMergedDataProvider > xBackend;

    UnoBackend xBackendService = createUnoBackend(_aSettings, _xCtx);

    if (xBackendService.is())
        xBackend = new BackendAccess(xBackendService, _xCtx);

    return xBackend;
}
// -------------------------------------------------------------------------

rtl::Reference<IMergedDataProvider>
    BackendFactory::createSessionBackend(IConfigSession * _pSession,
                                            TypeConverterRef const & _xTCV)
{
    rtl::Reference< IMergedDataProvider > xBackend;

    if (_pSession)
        xBackend = wrapSession(*_pSession,_xTCV);

    return xBackend;
}
// -------------------------------------------------------------------------

BackendFactory & BackendFactory::instance()
{
    static BackendFactory aStaticFactory;
    return aStaticFactory;
}

//-----------------------------------------------------------------------------
    } // namespace
//-----------------------------------------------------------------------------
} // namespace
<|endoftext|>