diff --git a/externals/vts-libs b/externals/vts-libs index 0e0bd09..408746b 160000 --- a/externals/vts-libs +++ b/externals/vts-libs @@ -1 +1 @@ -Subproject commit 0e0bd095dc44698e682c485136924e36b7fcc45f +Subproject commit 408746ba75d32189399ba7eec395805b767d7a14 diff --git a/mapproxy/src/mapproxy/CMakeLists.txt b/mapproxy/src/mapproxy/CMakeLists.txt index 8cd82a3..66e1440 100644 --- a/mapproxy/src/mapproxy/CMakeLists.txt +++ b/mapproxy/src/mapproxy/CMakeLists.txt @@ -130,6 +130,8 @@ set(mapproxy_SOURCES generator.hpp generator/generator.cpp + generator/generators.hpp generator/generators.cpp + generator/registry.hpp generator/registry.cpp generator/factory.hpp generator/metatile.hpp generator/metatile.cpp generator/demregistry.hpp generator/demregistry.cpp diff --git a/mapproxy/src/mapproxy/generator/factory.hpp b/mapproxy/src/mapproxy/generator/factory.hpp index 3ac8aa8..0e40f49 100644 --- a/mapproxy/src/mapproxy/generator/factory.hpp +++ b/mapproxy/src/mapproxy/generator/factory.hpp @@ -40,6 +40,17 @@ struct Generator::Factory { * reference frame. */ virtual bool systemInstance() const { return false; } + + /** Factory registry support + */ + typedef std::map Registry; + + static pointer findFactory(const Resource::Generator &type); + + static void registerType(const Resource::Generator &type + , const pointer &factory); + + static Registry ®istry(); }; #endif // mapproxy_generator_factory_hpp_included_ diff --git a/mapproxy/src/mapproxy/generator/generator.cpp b/mapproxy/src/mapproxy/generator/generator.cpp index 03115f6..1fb25fd 100644 --- a/mapproxy/src/mapproxy/generator/generator.cpp +++ b/mapproxy/src/mapproxy/generator/generator.cpp @@ -59,30 +59,12 @@ namespace { const std::string ResourceFile("resource.json"); -typedef std::map Registry; - -Registry& registry() { - static Registry registry; - return registry; -} - -Generator::Factory::pointer findFactory(const Resource::Generator &type) -{ - const auto &r(registry()); - auto fregistry(r.find(type)); - if (fregistry == r.end()) { - LOGTHROW(err1, UnknownGenerator) - << "Unknown generator type <" << type << ">."; - } - return fregistry->second; -} - } // namespace void Generator::registerType(const Resource::Generator &type , const Factory::pointer &factory) { - registry().insert(Registry::value_type(type, factory)); + Factory::registerType(type, factory); } DefinitionBase::pointer Generator::definition(const Resource::Generator &type) @@ -93,7 +75,7 @@ DefinitionBase::pointer Generator::definition(const Resource::Generator &type) Generator::pointer Generator::create(const Params ¶ms) { try { - return findFactory(params.resource.generator)->create(params); + return Factory::findFactory(params.resource.generator)->create(params); } catch (const boost::bad_any_cast&) { LOGTHROW(err2, InvalidConfiguration) << "Passed resource does not match generator <" @@ -314,864 +296,6 @@ bool Generator::updatedSince(std::uint64_t timestamp) const return readySince_ > timestamp; } -namespace { - -struct TypeKey { - std::string referenceFrame; - Resource::Generator::Type type; - - bool operator<(const TypeKey &o) const { - if (referenceFrame < o.referenceFrame) { return true; } - if (o.referenceFrame < referenceFrame) { return false; } - return type < o.type; - } - - TypeKey(const std::string &referenceFrame, Resource::Generator::Type type) - : referenceFrame(referenceFrame), type(type) - {} -}; - -TypeKey extractTypeKey(const Generator &generator) -{ - const auto r(generator.resource()); - return { r.id.referenceFrame, r.generator.type }; -} - -struct GroupKey { - std::string referenceFrame; - Resource::Generator::Type type; - std::string group; - - bool operator<(const GroupKey &o) const { - if (referenceFrame < o.referenceFrame) { return true; } - if (o.referenceFrame < referenceFrame) { return false; } - - if (group < o.group) { return true; } - if (o.group < group) { return false; } - - return type < o.type; - } - - GroupKey(const std::string &referenceFrame, Resource::Generator::Type type - , const std::string &group) - : referenceFrame(referenceFrame), type(type), group(group) - {} -}; - -GroupKey extractGroupKey(const Generator &generator) -{ - const auto r(generator.resource()); - return { r.id.referenceFrame, r.generator.type, r.id.group }; -} - -Generators::Config fixUp(Generators::Config config) -{ - // sanity check - if (!config.externalUrl.empty() && (config.externalUrl.back() != '/')) { - config.externalUrl.push_back('/'); - } - return config; -} - -} // namespace - -class Generators::Detail - : public boost::noncopyable - , public GeneratorFinder -{ -public: - Detail(const Generators::Config &config - , const ResourceBackend::pointer &resourceBackend) - : config_(fixUp(config)), resourceBackend_(resourceBackend) - , arsenal_(), running_(false), updateRequest_(false), lastUpdate_(0) - , ready_(false), preparing_(0) - , work_(ios_), demRegistry_(std::make_shared()) - { - registerSystemGenerators(); - } - - void checkReady() const; - - Generator::pointer generator(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const; - - Generator::list referenceFrame(const std::string &referenceFrame) const; - - std::vector listGroups(const std::string &referenceFrame - , Resource::Generator::Type type) - const; - - std::vector listIds(const std::string &referenceFrame - , Resource::Generator::Type type - , const std::string &group) const; - - void start(Arsenal &arsenal); - void stop(); - - inline const Config& config() const { return config_; } - - inline const DemRegistry& demRegistry() const { return *demRegistry_; } - - std::uint64_t update(); - - bool updatedSince(std::uint64_t timestamp) const; - - void listResources(std::ostream &os) const; - - void replace(const Generator::pointer &original - , const Generator::pointer &replacement); - - bool has(const Resource::Id &resourceId) const; - - bool isReady(const Resource::Id &resourceId) const; - - std::string url(const Resource::Id &resourceId - , GeneratorInterface::Interface iface) const; - - bool updatedSince(const Resource::Id &resourceId - , std::uint64_t timestamp, bool nothrow) const; - -private: - void registerSystemGenerators(); - - void update(const Resource::map &resources); - - void updater(); - void worker(std::size_t id); - void prepare(const Generator::pointer &generator); - - virtual Generator::pointer - findGenerator_impl(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const; - - const Config config_; - ResourceBackend::pointer resourceBackend_; - Arsenal *arsenal_; - - // resource updater stuff - std::thread updater_; - std::atomic running_; - std::atomic updateRequest_; - std::atomic lastUpdate_; - std::mutex updaterLock_; - std::condition_variable updaterCond_; - - struct ResourceIdIdx {}; - struct GroupIdx {}; - struct TypeIdx {}; - struct ReferenceFrameIdx {}; - - typedef boost::multi_index_container< - Generator::pointer - , bmi::indexed_by< - bmi::ordered_unique > - - , bmi::ordered_unique< - bmi::tag - , BOOST_MULTI_INDEX_CONST_MEM_FUN - (Generator, const Resource::Id&, id) - > - - , bmi::ordered_non_unique< - bmi::tag - , bmi::global_fun - > - - , bmi::ordered_non_unique< - bmi::tag - , bmi::global_fun - > - - , bmi::ordered_non_unique< - bmi::tag - , BOOST_MULTI_INDEX_CONST_MEM_FUN - (Generator, const std::string&, referenceFrameId) - > - > - - > GeneratorMap; - - - - // internals - mutable std::mutex lock_; - GeneratorMap serving_; - - std::atomic ready_; - std::atomic preparing_; - - // prepare stuff - asio::io_service ios_; - asio::io_service::work work_; - std::vector workers_; - - // DEM registry - DemRegistry::pointer demRegistry_; -}; - -void Generators::Detail::checkReady() const -{ - if (ready_) { return; } - throw Unavailable("Server not ready."); -} - -void Generators::Detail::start(Arsenal &arsenal) -{ - // make sure threads are released when something goes wrong - struct Guard { - Guard(const std::function &func) : func(func) {} - ~Guard() { if (func) { func(); } } - void release() { func = {}; } - std::function func; - } guard([this]() { stop(); }); - - // start updater - running_ = true; - std::thread updater(&Detail::updater, this); - updater_.swap(updater); - - arsenal_ = &arsenal; - // TODO: make configurable - std::size_t count(5); - // start workers - for (std::size_t id(1); id <= count; ++id) { - workers_.emplace_back(&Detail::worker, this, id); - } - - guard.release(); -} - -void Generators::Detail::stop() -{ - if (!running_) { return; } - - running_ = false; - ios_.stop(); - - updaterCond_.notify_all(); - updater_.join(); - - while (!workers_.empty()) { - workers_.back().join(); - workers_.pop_back(); - } - arsenal_ = {}; -} - -struct Aborted {}; - -void Generators::Detail::updater() -{ - dbglog::thread_id("updater"); - - // invalidate any update request - updateRequest_ = false; - // never update - lastUpdate_ = 0; - - while (running_) { - // default sleep time in seconds - std::chrono::seconds sleep(config_.resourceUpdatePeriod); - - try { - update(resourceBackend_->load()); - lastUpdate_ = utility::usecFromEpoch(); - } catch (Aborted) { - // pass - } catch (const std::exception &e) { - LOG(err2) << "Resource info update failed: <" << e.what() << ">."; - if (config_.resourceUpdatePeriod > 0) { - sleep = std::chrono::seconds(5); - } - } - - // sleep for configured time minutes - { - std::unique_lock lock(updaterLock_); - - // condition variable wait predicate - const auto predicate([this]() -> bool - { - auto updateRequest - (std::atomic_exchange(&updateRequest_, false)); - return !running_ || updateRequest; - }); - - if (config_.resourceUpdatePeriod > 0) { - // wait for given duration - updaterCond_.wait_for(lock, sleep, predicate); - } else { - // wait untill bugged - updaterCond_.wait(lock, predicate); - } - } - } -} - -void Generators::Detail::worker(std::size_t id) -{ - dbglog::thread_id(str(boost::format("prepare:%u") % id)); - LOG(info2) << "Spawned prepare worker id:" << id << "."; - - for (;;) { - try { - ios_.run(); - LOG(info2) << "Terminated prepare worker id:" << id << "."; - return; - } catch (const std::exception &e) { - LOG(err3) - << "Uncaught exception in worker: <" << e.what() - << ">. Going on."; - } - } -} - -void Generators::Detail::prepare(const Generator::pointer &generator) -{ - ++preparing_; - - ios_.post([=]() - { - try { - generator->prepare(*arsenal_); - if (auto original = generator->replace()) { - replace(original, generator); - } - } catch (const std::exception &e) { - LOG(warn2) - << "Failed to prepare generator for <" - << generator->resource().id << "> (" << e.what() - << "); removing from set of known generators."; - - resourceBackend_->error(generator->resource().id, e.what()); - - // erease from map (under lock) - std::unique_lock lock(lock_); - serving_.erase(generator); - } - --preparing_; - }); -} - -void Generators::Detail::registerSystemGenerators() -{ - for (const auto &ritem : registry()) { - const auto &resourceGenerator(ritem.first); - const auto &factory(ritem.second); - - if (!factory->systemInstance()) { continue; } - - for (const auto &rfitem : vr::system.referenceFrames) { - const auto &rfId(rfitem.first); - const auto &rf(rfitem.second); - LOG(info2) << "About to register " << resourceGenerator - << " generator for" - << " reference frame " << rfitem.first << "."; - - // create resource - Resource resource - (resourceBackend_->genericConfig().fileClassSettings); - resource.id = Resource::Id - (rfId, Generator::systemGroup(), resourceGenerator.driver); - resource.generator = resourceGenerator; - resource.comment = "autoregistered resource"; - resource.referenceFrame = &rf; - resource.definition(resource::definition(resourceGenerator)); - - // start at first valid lod - // lod 22 is arbitrarily deep - resource.lodRange - = vts::LodRange(rf.division.rootLodRange.min, 22); - - // compose lod range from first valid lode - resource.tileRange = vts::TileRange(math::InvalidExtents{}); - for (const auto &item : rf.division.nodes) { - const auto &node(item.second); - if (!node.real()) { continue; } - if (node.id.lod == rf.division.rootLodRange.min) { - math::update(resource.tileRange, node.id.x, node.id.y); - } - } - - // create generator params - Generator::Params params(resource); - params.config = config_; - params.config.root = config_.root; - params.generatorFinder = this; - params.demRegistry = demRegistry_; - params.system = true; - - // create generator - auto g(factory->create(params)); - - // register - serving_.insert(g); - - // and prepare if not ready - if (!g->ready()) { - prepare(g); - } - } - } -} - -Generators::Generators(const Config &config - , const ResourceBackend::pointer &resourceBackend) - : detail_(std::make_shared(config, resourceBackend)) -{} - -Generators::~Generators() -{ - detail().stop(); -} - -void Generators::start(Arsenal &arsenal) -{ - detail().start(arsenal); -} - -void Generators::stop() -{ - detail().stop(); -} - -Generator::list Generators::referenceFrame(const std::string &referenceFrame) - const -{ - return detail().referenceFrame(referenceFrame); -} - -Generator::pointer -Generators::generator(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const -{ - return detail().generator(generatorType, resourceId); -} - -void Generators::Detail::replace(const Generator::pointer &original - , const Generator::pointer &replacement) -{ - std::unique_lock lock(lock_); - // find original in the serving set - auto ioriginal(serving_.find(original)); - // and replace - serving_.replace(ioriginal, replacement); - LOG(info3) - << "Replaced resource <" << original->id() << "> with new definiton."; -} - -void Generators::Detail::update(const Resource::map &resources) -{ - LOG(info2) << "Updating resources."; - - auto iresources(resources.begin()), eresources(resources.end()); - auto &idx(serving_.get()); - auto iserving(idx.begin()), eserving(idx.end()); - - Generator::list toAdd; - Generator::list toRemove; - Generator::list toReplace; - - auto add([&](const Resource &res) - { - if (!running_) { - throw Aborted{}; - } - try { - Generator::Params params(res); - params.config = config_; - params.config.root = config_.root; - params.generatorFinder = this; - params.demRegistry = demRegistry_; - toAdd.push_back(Generator::create(params)); - } catch (const std::exception &e) { - LOG(err2) << "Failed to create generator for resource <" - << iresources->first << ">: <" << e.what() << ">."; - } - }); - - auto replace([&](const Resource &res, const Generator::pointer &original) - { - if (!running_) { - throw Aborted{}; - } - try { - Generator::Params params(res); - params.config = config_; - params.config.root = config_.root; - params.generatorFinder = this; - params.demRegistry = demRegistry_; - params.replace = original; - toReplace.push_back(Generator::create(params)); - } catch (const std::exception &e) { - LOG(err2) << "Failed to re-create generator for resource <" - << iresources->first << ">: <" << e.what() << ">."; - } - }); - - // process common stuff - while ((iresources != eresources) && (iserving != eserving)) { - const auto &resource(iresources->second); - if (iresources->first < (*iserving)->id()) { - // new resource - add(resource); - ++iresources; - } else if ((*iserving)->id() < iresources->first) { - // removed resource - if (!(*iserving)->system()) { - toRemove.push_back(*iserving); - } - ++iserving; - } else { - // existing resource - switch ((*iserving)->changed(resource)) { - case Changed::no: - // same stuff, do nothing - break; - - case Changed::yes: - // changed - if (!config().freezes(resource.generator.type)) { - replace(resource, *iserving); - } - break; - - case Changed::safely: - case Changed::withRevisionBump: - // here comes the fun - replace(resource, *iserving); - break; - } - - ++iresources; - ++iserving; - } - } - - // process tail: added resources - for (; iresources != eresources; ++iresources) { - add(iresources->second); - } - - // process tail: removed resources - for (; iserving != eserving; ++iserving) { - if (!(*iserving)->system()) { - toRemove.push_back(*iserving); - } - } - - // add stuff - for (const auto &generator : toAdd) { - { - std::unique_lock lock(lock_); - serving_.insert(generator); - } - - if (!generator->ready()) { - prepare(generator); - } - } - - // remove stuff - for (const auto &generator : toRemove) { - { - std::unique_lock lock(lock_); - serving_.erase(generator); - } - - // TODO: mark as to be removed for prepare workers - } - - // replace stuff (prepare) - for (const auto &generator : toReplace) { - if (!generator->ready()) { - prepare(generator); - } else { - this->replace(generator->replace(), generator); - } - } - - LOG(info2) << "Resources updated."; - if (!ready_) { - ready_ = true; - LOG(info3) << "Ready to serve."; - } - - // wait till all pending resources are available; not nice but should work - while (preparing_ && running_) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } -} - -Generator::list -Generators::Detail::referenceFrame(const std::string &referenceFrame) - const -{ - checkReady(); - - Generator::list out; - - // use only ready generators that handle datasets for given reference frame - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - for (auto range(idx.equal_range(referenceFrame)); - range.first != range.second; ++range.first) - { - if ((*range.first)->ready()) { - out.push_back(*range.first); - } - } - - return out; -} - -Generator::pointer -Generators::Detail::generator(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) - const -{ - checkReady(); - - // find generator (under lock) - auto generator([&]() -> Generator::pointer - { - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - auto fserving(idx.find(resourceId)); - if (fserving == idx.end()) { return {}; } - return *fserving; - }()); - - if (!generator) { return generator; } - - const auto &resource(generator->resource()); - - // check generator type - if (generatorType != resource.generator.type) { - return {}; - } - - return generator; -} - -Generator::pointer -Generators::Detail::findGenerator_impl(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const -{ - auto g(generator(generatorType, resourceId)); - if (!g || !g->ready()) { return {}; } - return g; -} - -const Generators::Config& Generators::config() const { - return detail().config(); -} - -std::vector -Generators::Detail::listGroups(const std::string &referenceFrame - , Resource::Generator::Type type) - const -{ - checkReady(); - - std::vector out; - { - std::unique_lock lock(lock_); - - auto &idx(serving_.get()); - std::string prev; - for (auto range(idx.equal_range(TypeKey(referenceFrame, type))); - range.first != range.second; ++range.first) - { - const auto &group((*range.first)->group()); - if (group != prev) { - out.push_back(group); - prev = group; - } - } - } - - return out; -} - -std::vector -Generators::Detail::listIds(const std::string &referenceFrame - , Resource::Generator::Type type - , const std::string &group) - const -{ - checkReady(); - - std::vector out; - { - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - for (auto range(idx.equal_range - (GroupKey(referenceFrame, type, group))); - range.first != range.second; ++range.first) - { - out.push_back((*range.first)->id().id); - } - } - - return out; -} - -std::vector -Generators::listGroups(const std::string &referenceFrame - , Resource::Generator::Type type) const -{ - return detail().listGroups(referenceFrame, type); -} - -std::vector -Generators::listIds(const std::string &referenceFrame - , Resource::Generator::Type type - , const std::string &group) const -{ - return detail().listIds(referenceFrame, type, group); -} - -void Generator::supportFile(const vs::SupportFile &support, Sink &sink - , const Sink::FileInfo &fileInfo) const -{ - if (!support.isTemplate) { - sink.content(support.data, support.size, fileInfo, false); - return; - } - - // expand and send - sink.content(support.expand(config_.variables, config_.defaults) - , fileInfo); -} - -const DemRegistry& Generators::demRegistry() const -{ - return detail().demRegistry(); -} - -std::uint64_t Generators::Detail::update() -{ - const auto start(utility::usecFromEpoch()); - updateRequest_ = true; - updaterCond_.notify_one(); - return start; -} - -std::uint64_t Generators::update() -{ - return detail().update(); -} - -bool Generators::Detail::updatedSince(std::uint64_t timestamp) const -{ - return lastUpdate_ > timestamp; -} - -bool Generators::updatedSince(std::uint64_t timestamp) const -{ - return detail().updatedSince(timestamp); -} - -bool Generators::Detail::has(const Resource::Id &resourceId) const -{ - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - auto fserving(idx.find(resourceId)); - return (fserving != idx.end()); -} - -bool Generators::has(const Resource::Id &resourceId) const -{ - return detail().has(resourceId); -} - -bool Generators::Detail::isReady(const Resource::Id &resourceId) const -{ - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - auto fserving(idx.find(resourceId)); - if (fserving == idx.end()) { return false; } - return (*fserving)->ready(); -} - -bool Generators::isReady(const Resource::Id &resourceId) const -{ - return detail().isReady(resourceId); -} - -std::string Generators::Detail::url(const Resource::Id &resourceId - , GeneratorInterface::Interface iface) - const -{ - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - auto fserving(idx.find(resourceId)); - if (fserving == idx.end()) { - LOGTHROW(err1, UnknownGenerator) - << "No such generator <" << resourceId << ">"; - } - return (*fserving)->url(iface); -} - -std::string Generators::url(const Resource::Id &resourceId - , GeneratorInterface::Interface iface) const -{ - return detail().url(resourceId, iface); -} - -bool Generators::Detail::updatedSince(const Resource::Id &resourceId - , std::uint64_t timestamp - , bool nothrow) const -{ - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - auto fserving(idx.find(resourceId)); - if (fserving == idx.end()) { - if (nothrow) { return false; } - LOGTHROW(err1, UnknownGenerator) - << "No such generator <" << resourceId << ">"; - } - return (*fserving)->updatedSince(timestamp); -} - -bool Generators::updatedSince(const Resource::Id &resourceId - , std::uint64_t timestamp, bool nothrow) const -{ - return detail().updatedSince(resourceId, timestamp, nothrow); -} - -void Generator::status(std::ostream &os) const -{ - os << "<" << id() - << "> (type <" << resource().generator << ">)" - << (ready_ ? "" : " not ready") - << "\n"; -} - -void Generators::Detail::listResources(std::ostream &os) const -{ - - Generator::list generators; - { - std::unique_lock lock(lock_); - for (const auto &generator : serving_) { - generators.push_back(generator); - } - } - - for (const auto &generator : generators) { - generator->status(os); - } -} - -void Generators::listResources(std::ostream &os) const -{ - detail().listResources(os); -} - Generator::Task Generator::generateFile(const FileInfo &fileInfo, Sink sink) const { diff --git a/mapproxy/src/mapproxy/generator/generators.cpp b/mapproxy/src/mapproxy/generator/generators.cpp new file mode 100644 index 0000000..9cf991f --- /dev/null +++ b/mapproxy/src/mapproxy/generator/generators.cpp @@ -0,0 +1,733 @@ +/** + * Copyright (c) 2017 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dbglog/dbglog.hpp" +#include "utility/path.hpp" +#include "utility/gccversion.hpp" +#include "utility/time.hpp" +#include "utility/raise.hpp" + +#include "../error.hpp" +#include "../definition.hpp" +#include "generators.hpp" +#include "factory.hpp" + +namespace fs = boost::filesystem; +namespace ba = boost::algorithm; + +namespace { + +Generators::Config fixUp(Generators::Config config) +{ + // sanity check + if (!config.externalUrl.empty() && (config.externalUrl.back() != '/')) { + config.externalUrl.push_back('/'); + } + return config; +} + +} // namespace + +Generators::Detail::Detail(const Generators::Config &config + , const ResourceBackend::pointer &resourceBackend) + : config_(fixUp(config)), resourceBackend_(resourceBackend) + , arsenal_(), running_(false), updateRequest_(false), lastUpdate_(0) + , ready_(false), preparing_(0) + , work_(ios_), demRegistry_(std::make_shared()) +{ + registerSystemGenerators(); +} + +void Generators::Detail::checkReady() const +{ + if (ready_) { return; } + throw Unavailable("Server not ready."); +} + +void Generators::Detail::start(Arsenal &arsenal) +{ + // make sure threads are released when something goes wrong + struct Guard { + Guard(const std::function &func) : func(func) {} + ~Guard() { if (func) { func(); } } + void release() { func = {}; } + std::function func; + } guard([this]() { stop(); }); + + // start updater + running_ = true; + std::thread updater(&Detail::updater, this); + updater_.swap(updater); + + arsenal_ = &arsenal; + // TODO: make configurable + std::size_t count(5); + // start workers + for (std::size_t id(1); id <= count; ++id) { + workers_.emplace_back(&Detail::worker, this, id); + } + + guard.release(); +} + +void Generators::Detail::stop() +{ + if (!running_) { return; } + + running_ = false; + ios_.stop(); + + updaterCond_.notify_all(); + updater_.join(); + + while (!workers_.empty()) { + workers_.back().join(); + workers_.pop_back(); + } + arsenal_ = {}; +} + +struct Aborted {}; + +void Generators::Detail::updater() +{ + dbglog::thread_id("updater"); + + // invalidate any update request + updateRequest_ = false; + // never update + lastUpdate_ = 0; + + while (running_) { + // default sleep time in seconds + std::chrono::seconds sleep(config_.resourceUpdatePeriod); + + try { + update(resourceBackend_->load()); + lastUpdate_ = utility::usecFromEpoch(); + } catch (Aborted) { + // pass + } catch (const std::exception &e) { + LOG(err2) << "Resource info update failed: <" << e.what() << ">."; + if (config_.resourceUpdatePeriod > 0) { + sleep = std::chrono::seconds(5); + } + } + + // sleep for configured time minutes + { + std::unique_lock lock(updaterLock_); + + // condition variable wait predicate + const auto predicate([this]() -> bool + { + auto updateRequest + (std::atomic_exchange(&updateRequest_, false)); + return !running_ || updateRequest; + }); + + if (config_.resourceUpdatePeriod > 0) { + // wait for given duration + updaterCond_.wait_for(lock, sleep, predicate); + } else { + // wait untill bugged + updaterCond_.wait(lock, predicate); + } + } + } +} + +void Generators::Detail::worker(std::size_t id) +{ + dbglog::thread_id(str(boost::format("prepare:%u") % id)); + LOG(info2) << "Spawned prepare worker id:" << id << "."; + + for (;;) { + try { + ios_.run(); + LOG(info2) << "Terminated prepare worker id:" << id << "."; + return; + } catch (const std::exception &e) { + LOG(err3) + << "Uncaught exception in worker: <" << e.what() + << ">. Going on."; + } + } +} + +void Generators::Detail::prepare(const Generator::pointer &generator) +{ + ++preparing_; + + ios_.post([=]() + { + try { + generator->prepare(*arsenal_); + if (auto original = generator->replace()) { + replace(original, generator); + } + } catch (const std::exception &e) { + LOG(warn2) + << "Failed to prepare generator for <" + << generator->resource().id << "> (" << e.what() + << "); removing from set of known generators."; + + resourceBackend_->error(generator->resource().id, e.what()); + + // erease from map (under lock) + std::unique_lock lock(lock_); + serving_.erase(generator); + } + --preparing_; + }); +} + +void Generators::Detail::registerSystemGenerators() +{ + for (const auto &ritem : Generator::Factory::registry()) { + const auto &resourceGenerator(ritem.first); + const auto &factory(ritem.second); + + if (!factory->systemInstance()) { continue; } + + for (const auto &rfitem : vr::system.referenceFrames) { + const auto &rfId(rfitem.first); + const auto &rf(rfitem.second); + LOG(info2) << "About to register " << resourceGenerator + << " generator for" + << " reference frame " << rfitem.first << "."; + + // create resource + Resource resource + (resourceBackend_->genericConfig().fileClassSettings); + resource.id = Resource::Id + (rfId, Generator::systemGroup(), resourceGenerator.driver); + resource.generator = resourceGenerator; + resource.comment = "autoregistered resource"; + resource.referenceFrame = &rf; + resource.definition(resource::definition(resourceGenerator)); + + // start at first valid lod + // lod 22 is arbitrarily deep + resource.lodRange + = vts::LodRange(rf.division.rootLodRange.min, 22); + + // compose lod range from first valid lode + resource.tileRange = vts::TileRange(math::InvalidExtents{}); + for (const auto &item : rf.division.nodes) { + const auto &node(item.second); + if (!node.real()) { continue; } + if (node.id.lod == rf.division.rootLodRange.min) { + math::update(resource.tileRange, node.id.x, node.id.y); + } + } + + // create generator params + Generator::Params params(resource); + params.config = config_; + params.config.root = config_.root; + params.generatorFinder = this; + params.demRegistry = demRegistry_; + params.system = true; + + // create generator + auto g(factory->create(params)); + + // register + serving_.insert(g); + + // and prepare if not ready + if (!g->ready()) { + prepare(g); + } + } + } +} + +Generators::Generators(const Config &config + , const ResourceBackend::pointer &resourceBackend) + : detail_(std::make_shared(config, resourceBackend)) +{} + +Generators::~Generators() +{ + detail().stop(); +} + +void Generators::start(Arsenal &arsenal) +{ + detail().start(arsenal); +} + +void Generators::stop() +{ + detail().stop(); +} + +Generator::list Generators::referenceFrame(const std::string &referenceFrame) + const +{ + return detail().referenceFrame(referenceFrame); +} + +Generator::pointer +Generators::generator(Resource::Generator::Type generatorType + , const Resource::Id &resourceId) const +{ + return detail().generator(generatorType, resourceId); +} + +void Generators::Detail::replace(const Generator::pointer &original + , const Generator::pointer &replacement) +{ + std::unique_lock lock(lock_); + // find original in the serving set + auto ioriginal(serving_.find(original)); + // and replace + serving_.replace(ioriginal, replacement); + LOG(info3) + << "Replaced resource <" << original->id() << "> with new definiton."; +} + +void Generators::Detail::update(const Resource::map &resources) +{ + LOG(info2) << "Updating resources."; + + auto iresources(resources.begin()), eresources(resources.end()); + auto &idx(serving_.get()); + auto iserving(idx.begin()), eserving(idx.end()); + + Generator::list toAdd; + Generator::list toRemove; + Generator::list toReplace; + + auto add([&](const Resource &res) + { + if (!running_) { + throw Aborted{}; + } + try { + Generator::Params params(res); + params.config = config_; + params.config.root = config_.root; + params.generatorFinder = this; + params.demRegistry = demRegistry_; + toAdd.push_back(Generator::create(params)); + } catch (const std::exception &e) { + LOG(err2) << "Failed to create generator for resource <" + << iresources->first << ">: <" << e.what() << ">."; + } + }); + + auto replace([&](const Resource &res, const Generator::pointer &original) + { + if (!running_) { + throw Aborted{}; + } + try { + Generator::Params params(res); + params.config = config_; + params.config.root = config_.root; + params.generatorFinder = this; + params.demRegistry = demRegistry_; + params.replace = original; + toReplace.push_back(Generator::create(params)); + } catch (const std::exception &e) { + LOG(err2) << "Failed to re-create generator for resource <" + << iresources->first << ">: <" << e.what() << ">."; + } + }); + + // process common stuff + while ((iresources != eresources) && (iserving != eserving)) { + const auto &resource(iresources->second); + if (iresources->first < (*iserving)->id()) { + // new resource + add(resource); + ++iresources; + } else if ((*iserving)->id() < iresources->first) { + // removed resource + if (!(*iserving)->system()) { + toRemove.push_back(*iserving); + } + ++iserving; + } else { + // existing resource + switch ((*iserving)->changed(resource)) { + case Changed::no: + // same stuff, do nothing + break; + + case Changed::yes: + // changed + if (!config().freezes(resource.generator.type)) { + replace(resource, *iserving); + } + break; + + case Changed::safely: + case Changed::withRevisionBump: + // here comes the fun + replace(resource, *iserving); + break; + } + + ++iresources; + ++iserving; + } + } + + // process tail: added resources + for (; iresources != eresources; ++iresources) { + add(iresources->second); + } + + // process tail: removed resources + for (; iserving != eserving; ++iserving) { + if (!(*iserving)->system()) { + toRemove.push_back(*iserving); + } + } + + // add stuff + for (const auto &generator : toAdd) { + { + std::unique_lock lock(lock_); + serving_.insert(generator); + } + + if (!generator->ready()) { + prepare(generator); + } + } + + // remove stuff + for (const auto &generator : toRemove) { + { + std::unique_lock lock(lock_); + serving_.erase(generator); + } + + // TODO: mark as to be removed for prepare workers + } + + // replace stuff (prepare) + for (const auto &generator : toReplace) { + if (!generator->ready()) { + prepare(generator); + } else { + this->replace(generator->replace(), generator); + } + } + + LOG(info2) << "Resources updated."; + if (!ready_) { + ready_ = true; + LOG(info3) << "Ready to serve."; + } + + // wait till all pending resources are available; not nice but should work + while (preparing_ && running_) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} + +Generator::list +Generators::Detail::referenceFrame(const std::string &referenceFrame) + const +{ + checkReady(); + + Generator::list out; + + // use only ready generators that handle datasets for given reference frame + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + for (auto range(idx.equal_range(referenceFrame)); + range.first != range.second; ++range.first) + { + if ((*range.first)->ready()) { + out.push_back(*range.first); + } + } + + return out; +} + +Generator::pointer +Generators::Detail::generator(Resource::Generator::Type generatorType + , const Resource::Id &resourceId) + const +{ + checkReady(); + + // find generator (under lock) + auto generator([&]() -> Generator::pointer + { + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + auto fserving(idx.find(resourceId)); + if (fserving == idx.end()) { return {}; } + return *fserving; + }()); + + if (!generator) { return generator; } + + const auto &resource(generator->resource()); + + // check generator type + if (generatorType != resource.generator.type) { + return {}; + } + + return generator; +} + +Generator::pointer +Generators::Detail::findGenerator_impl(Resource::Generator::Type generatorType + , const Resource::Id &resourceId) const +{ + auto g(generator(generatorType, resourceId)); + if (!g || !g->ready()) { return {}; } + return g; +} + +const Generators::Config& Generators::config() const { + return detail().config(); +} + +std::vector +Generators::Detail::listGroups(const std::string &referenceFrame + , Resource::Generator::Type type) + const +{ + checkReady(); + + std::vector out; + { + std::unique_lock lock(lock_); + + auto &idx(serving_.get()); + std::string prev; + for (auto range(idx.equal_range(Keys::TypeKey(referenceFrame, type))); + range.first != range.second; ++range.first) + { + const auto &group((*range.first)->group()); + if (group != prev) { + out.push_back(group); + prev = group; + } + } + } + + return out; +} + +std::vector +Generators::Detail::listIds(const std::string &referenceFrame + , Resource::Generator::Type type + , const std::string &group) + const +{ + checkReady(); + + std::vector out; + { + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + for (auto range(idx.equal_range + (Keys::GroupKey(referenceFrame, type, group))); + range.first != range.second; ++range.first) + { + out.push_back((*range.first)->id().id); + } + } + + return out; +} + +std::vector +Generators::listGroups(const std::string &referenceFrame + , Resource::Generator::Type type) const +{ + return detail().listGroups(referenceFrame, type); +} + +std::vector +Generators::listIds(const std::string &referenceFrame + , Resource::Generator::Type type + , const std::string &group) const +{ + return detail().listIds(referenceFrame, type, group); +} + +void Generator::supportFile(const vs::SupportFile &support, Sink &sink + , const Sink::FileInfo &fileInfo) const +{ + if (!support.isTemplate) { + sink.content(support.data, support.size, fileInfo, false); + return; + } + + // expand and send + sink.content(support.expand(config_.variables, config_.defaults) + , fileInfo); +} + +const DemRegistry& Generators::demRegistry() const +{ + return detail().demRegistry(); +} + +std::uint64_t Generators::Detail::update() +{ + const auto start(utility::usecFromEpoch()); + updateRequest_ = true; + updaterCond_.notify_one(); + return start; +} + +std::uint64_t Generators::update() +{ + return detail().update(); +} + +bool Generators::Detail::updatedSince(std::uint64_t timestamp) const +{ + return lastUpdate_ > timestamp; +} + +bool Generators::updatedSince(std::uint64_t timestamp) const +{ + return detail().updatedSince(timestamp); +} + +bool Generators::Detail::has(const Resource::Id &resourceId) const +{ + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + auto fserving(idx.find(resourceId)); + return (fserving != idx.end()); +} + +bool Generators::has(const Resource::Id &resourceId) const +{ + return detail().has(resourceId); +} + +bool Generators::Detail::isReady(const Resource::Id &resourceId) const +{ + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + auto fserving(idx.find(resourceId)); + if (fserving == idx.end()) { return false; } + return (*fserving)->ready(); +} + +bool Generators::isReady(const Resource::Id &resourceId) const +{ + return detail().isReady(resourceId); +} + +std::string Generators::Detail::url(const Resource::Id &resourceId + , GeneratorInterface::Interface iface) + const +{ + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + auto fserving(idx.find(resourceId)); + if (fserving == idx.end()) { + LOGTHROW(err1, UnknownGenerator) + << "No such generator <" << resourceId << ">"; + } + return (*fserving)->url(iface); +} + +std::string Generators::url(const Resource::Id &resourceId + , GeneratorInterface::Interface iface) const +{ + return detail().url(resourceId, iface); +} + +bool Generators::Detail::updatedSince(const Resource::Id &resourceId + , std::uint64_t timestamp + , bool nothrow) const +{ + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + auto fserving(idx.find(resourceId)); + if (fserving == idx.end()) { + if (nothrow) { return false; } + LOGTHROW(err1, UnknownGenerator) + << "No such generator <" << resourceId << ">"; + } + return (*fserving)->updatedSince(timestamp); +} + +bool Generators::updatedSince(const Resource::Id &resourceId + , std::uint64_t timestamp, bool nothrow) const +{ + return detail().updatedSince(resourceId, timestamp, nothrow); +} + +void Generator::status(std::ostream &os) const +{ + os << "<" << id() + << "> (type <" << resource().generator << ">)" + << (ready_ ? "" : " not ready") + << "\n"; +} + +void Generators::Detail::listResources(std::ostream &os) const +{ + + Generator::list generators; + { + std::unique_lock lock(lock_); + for (const auto &generator : serving_) { + generators.push_back(generator); + } + } + + for (const auto &generator : generators) { + generator->status(os); + } +} + +void Generators::listResources(std::ostream &os) const +{ + detail().listResources(os); +} diff --git a/mapproxy/src/mapproxy/generator/generators.hpp b/mapproxy/src/mapproxy/generator/generators.hpp new file mode 100644 index 0000000..153bbfd --- /dev/null +++ b/mapproxy/src/mapproxy/generator/generators.hpp @@ -0,0 +1,221 @@ +/** + * Copyright (c) 2019 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef mapproxy_generator_generators_hpp_included_ +#define mapproxy_generator_generators_hpp_included_ + +#include +#include +#include +#include +#include + +#include "../generator.hpp" + +namespace asio = boost::asio; +namespace bmi = boost::multi_index; + +namespace Keys { + +struct TypeKey { + std::string referenceFrame; + Resource::Generator::Type type; + + bool operator<(const TypeKey &o) const { + if (referenceFrame < o.referenceFrame) { return true; } + if (o.referenceFrame < referenceFrame) { return false; } + return type < o.type; + } + + TypeKey(const std::string &referenceFrame, Resource::Generator::Type type) + : referenceFrame(referenceFrame), type(type) + {} + + static TypeKey extract(const Generator &generator) { + const auto r(generator.resource()); + return { r.id.referenceFrame, r.generator.type }; + } +}; + +struct GroupKey { + std::string referenceFrame; + Resource::Generator::Type type; + std::string group; + + bool operator<(const GroupKey &o) const { + if (referenceFrame < o.referenceFrame) { return true; } + if (o.referenceFrame < referenceFrame) { return false; } + + if (group < o.group) { return true; } + if (o.group < group) { return false; } + + return type < o.type; + } + + GroupKey(const std::string &referenceFrame, Resource::Generator::Type type + , const std::string &group) + : referenceFrame(referenceFrame), type(type), group(group) + {} + + static GroupKey extract(const Generator &generator) { + const auto r(generator.resource()); + return { r.id.referenceFrame, r.generator.type, r.id.group }; + } + +}; + +} // namespace Keys + +class Generators::Detail + : public boost::noncopyable + , public GeneratorFinder +{ +public: + Detail(const Generators::Config &config + , const ResourceBackend::pointer &resourceBackend); + + void checkReady() const; + + Generator::pointer generator(Resource::Generator::Type generatorType + , const Resource::Id &resourceId) const; + + Generator::list referenceFrame(const std::string &referenceFrame) const; + + std::vector listGroups(const std::string &referenceFrame + , Resource::Generator::Type type) + const; + + std::vector listIds(const std::string &referenceFrame + , Resource::Generator::Type type + , const std::string &group) const; + + void start(Arsenal &arsenal); + void stop(); + + inline const Config& config() const { return config_; } + + inline const DemRegistry& demRegistry() const { return *demRegistry_; } + + std::uint64_t update(); + + bool updatedSince(std::uint64_t timestamp) const; + + void listResources(std::ostream &os) const; + + void replace(const Generator::pointer &original + , const Generator::pointer &replacement); + + bool has(const Resource::Id &resourceId) const; + + bool isReady(const Resource::Id &resourceId) const; + + std::string url(const Resource::Id &resourceId + , GeneratorInterface::Interface iface) const; + + bool updatedSince(const Resource::Id &resourceId + , std::uint64_t timestamp, bool nothrow) const; + +private: + void registerSystemGenerators(); + + void update(const Resource::map &resources); + + void updater(); + void worker(std::size_t id); + void prepare(const Generator::pointer &generator); + + virtual Generator::pointer + findGenerator_impl(Resource::Generator::Type generatorType + , const Resource::Id &resourceId) const; + + const Config config_; + ResourceBackend::pointer resourceBackend_; + Arsenal *arsenal_; + + // resource updater stuff + std::thread updater_; + std::atomic running_; + std::atomic updateRequest_; + std::atomic lastUpdate_; + std::mutex updaterLock_; + std::condition_variable updaterCond_; + + struct ResourceIdIdx {}; + struct GroupIdx {}; + struct TypeIdx {}; + struct ReferenceFrameIdx {}; + + typedef boost::multi_index_container< + Generator::pointer + , bmi::indexed_by< + bmi::ordered_unique > + + , bmi::ordered_unique< + bmi::tag + , BOOST_MULTI_INDEX_CONST_MEM_FUN + (Generator, const Resource::Id&, id) + > + + , bmi::ordered_non_unique< + bmi::tag + , bmi::global_fun + > + + , bmi::ordered_non_unique< + bmi::tag + , bmi::global_fun + > + + , bmi::ordered_non_unique< + bmi::tag + , BOOST_MULTI_INDEX_CONST_MEM_FUN + (Generator, const std::string&, referenceFrameId) + > + > + + > GeneratorMap; + + + + // internals + mutable std::mutex lock_; + GeneratorMap serving_; + + std::atomic ready_; + std::atomic preparing_; + + // prepare stuff + asio::io_service ios_; + asio::io_service::work work_; + std::vector workers_; + + // DEM registry + DemRegistry::pointer demRegistry_; +}; + +#endif // mapproxy_generator_generators_hpp_included_ diff --git a/mapproxy/src/mapproxy/generator/registry.cpp b/mapproxy/src/mapproxy/generator/registry.cpp new file mode 100644 index 0000000..a226561 --- /dev/null +++ b/mapproxy/src/mapproxy/generator/registry.cpp @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2019 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "factory.hpp" + +Generator::Factory::pointer +Generator::Factory::findFactory(const Resource::Generator &type) +{ + const auto &r(registry()); + auto fregistry(r.find(type)); + if (fregistry == r.end()) { + LOGTHROW(err1, UnknownGenerator) + << "Unknown generator type <" << type << ">."; + } + return fregistry->second; +} + +void Generator::Factory::registerType(const Resource::Generator &type + , const pointer &factory) +{ + registry().insert(Registry::value_type(type, factory)); +} + +Generator::Factory::Registry& Generator::Factory::registry() +{ + static Registry registry; + return registry; +}