/* * Copyright (C) 2014 Christian Mollekopf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "resourcefacade.h" #include "resourceconfig.h" #include "query.h" #include "definitions.h" #include "storage.h" #include "resourceaccess.h" #include using namespace Sink; template ConfigNotifier LocalStorageFacade::sConfigNotifier; template static typename DomainType::Ptr readFromConfig(ConfigStore &configStore, const QByteArray &id, const QByteArray &type) { auto object = DomainType::Ptr::create(id); object->setProperty("type", type); const auto configurationValues = configStore.get(id); for (auto it = configurationValues.constBegin(); it != configurationValues.constEnd(); it++) { object->setProperty(it.key(), it.value()); } return object; } static bool matchesFilter(const QHash &filter, const QMap &properties) { for (const auto &filterProperty : filter.keys()) { if (filterProperty == "type") { continue; } if (!filter.value(filterProperty).matches(properties.value(filterProperty))) { return false; } } return true; } template LocalStorageQueryRunner::LocalStorageQueryRunner(const Query &query, const QByteArray &identifier, ConfigNotifier &configNotifier) : mResultProvider(new ResultProvider), mConfigStore(identifier) { QObject *guard = new QObject; mResultProvider->setFetcher([this, query, guard, &configNotifier](const QSharedPointer &) { const auto entries = mConfigStore.getEntries(); for (const auto &res : entries.keys()) { const auto type = entries.value(res); if (query.propertyFilter.contains("type") && query.propertyFilter.value("type").value.toByteArray() != type) { Trace() << "Skipping due to type."; continue; } if (!query.ids.isEmpty() && !query.ids.contains(res)) { continue; } const auto configurationValues = mConfigStore.get(res); if (!matchesFilter(query.propertyFilter, configurationValues)){ Trace() << "Skipping due to filter."; continue; } Trace() << "Found match " << res; auto entity = readFromConfig(mConfigStore, res, type); updateStatus(*entity); mResultProvider->add(entity); } if (query.liveQuery) { { auto ret = QObject::connect(&configNotifier, &ConfigNotifier::added, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { auto entity = entry.staticCast(); updateStatus(*entity); mResultProvider->add(entity); }); Q_ASSERT(ret); } { auto ret = QObject::connect(&configNotifier, &ConfigNotifier::modified, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { auto entity = entry.staticCast(); updateStatus(*entity); mResultProvider->modify(entity); }); Q_ASSERT(ret); } { auto ret = QObject::connect(&configNotifier, &ConfigNotifier::removed, guard, [this](const ApplicationDomain::ApplicationDomainType::Ptr &entry) { mResultProvider->remove(entry.staticCast()); }); Q_ASSERT(ret); } } // TODO initialResultSetComplete should be implicit mResultProvider->initialResultSetComplete(typename DomainType::Ptr()); mResultProvider->complete(); }); mResultProvider->onDone([=]() { delete guard; }); } // QByteArrayList getMatchingEntries(const Query &query) // { // const auto entries = mConfigStore.getEntries(); // for (const auto &res : entries.keys()) { // const auto type = entries.value(res); // // if (query.propertyFilter.contains("type") && query.propertyFilter.value("type").value.toByteArray() != type) { // Trace() << "Skipping due to type."; // continue; // } // if (!query.ids.isEmpty() && !query.ids.contains(res)) { // continue; // } // const auto configurationValues = mConfigStore.get(res); // if (!matchesFilter(query.propertyFilter, configurationValues)){ // Trace() << "Skipping due to filter."; // continue; // } // Trace() << "Found match " << res; // auto entity = readFromConfig(mConfigStore, res, type); // updateStatus(*entity); // mResultProvider->add(entity); // } // // } template void LocalStorageQueryRunner::updateStatus(DomainType &entity) { if (mStatusUpdater) { mStatusUpdater(entity); } } template void LocalStorageQueryRunner::setStatusUpdater(const std::function &updater) { mStatusUpdater = updater; } template void LocalStorageQueryRunner::statusChanged(const QByteArray &identifier) { Trace() << "Status changed " << identifier; auto entity = readFromConfig(mConfigStore, identifier, ApplicationDomain::getTypeName()); updateStatus(*entity); mResultProvider->modify(entity); } template typename Sink::ResultEmitter::Ptr LocalStorageQueryRunner::emitter() { return mResultProvider->emitter(); } template LocalStorageFacade::LocalStorageFacade(const QByteArray &identifier) : StoreFacade(), mIdentifier(identifier), mConfigStore(identifier) { } template LocalStorageFacade::~LocalStorageFacade() { } template typename DomainType::Ptr LocalStorageFacade::readFromConfig(const QByteArray &id, const QByteArray &type) { return ::readFromConfig(mConfigStore, id, type); } template KAsync::Job LocalStorageFacade::create(const DomainType &domainObject) { return KAsync::start([domainObject, this]() { const QByteArray type = domainObject.getProperty("type").toByteArray(); const QByteArray providedIdentifier = domainObject.identifier().isEmpty() ? domainObject.getProperty("identifier").toByteArray() : domainObject.identifier(); const QByteArray identifier = providedIdentifier.isEmpty() ? ResourceConfig::newIdentifier(type) : providedIdentifier; mConfigStore.add(identifier, type); auto changedProperties = domainObject.changedProperties(); changedProperties.removeOne("identifier"); changedProperties.removeOne("type"); if (!changedProperties.isEmpty()) { // We have some configuration values QMap configurationValues; for (const auto &property : changedProperties) { configurationValues.insert(property, domainObject.getProperty(property)); } mConfigStore.modify(identifier, configurationValues); } sConfigNotifier.add(readFromConfig(identifier, type)); }); } template KAsync::Job LocalStorageFacade::modify(const DomainType &domainObject) { return KAsync::start([domainObject, this]() { const QByteArray identifier = domainObject.identifier(); if (identifier.isEmpty()) { Warning() << "We need an \"identifier\" property to identify the entity to configure."; return; } auto changedProperties = domainObject.changedProperties(); changedProperties.removeOne("identifier"); changedProperties.removeOne("type"); if (!changedProperties.isEmpty()) { // We have some configuration values QMap configurationValues; for (const auto &property : changedProperties) { configurationValues.insert(property, domainObject.getProperty(property)); } mConfigStore.modify(identifier, configurationValues); } const auto type = mConfigStore.getEntries().value(identifier); sConfigNotifier.modify(readFromConfig(identifier, type)); }); } template KAsync::Job LocalStorageFacade::remove(const DomainType &domainObject) { return KAsync::start([domainObject, this]() { const QByteArray identifier = domainObject.identifier(); if (identifier.isEmpty()) { Warning() << "We need an \"identifier\" property to identify the entity to configure"; return; } Trace() << "Removing: " << identifier; mConfigStore.remove(identifier); sConfigNotifier.remove(QSharedPointer::create(domainObject)); }); } template QPair, typename ResultEmitter::Ptr> LocalStorageFacade::load(const Query &query) { auto runner = new LocalStorageQueryRunner(query, mIdentifier, sConfigNotifier); return qMakePair(KAsync::null(), runner->emitter()); } ResourceFacade::ResourceFacade() : LocalStorageFacade("resources") { } ResourceFacade::~ResourceFacade() { } KAsync::Job ResourceFacade::remove(const Sink::ApplicationDomain::SinkResource &resource) { const auto identifier = resource.identifier(); return LocalStorageFacade::remove(resource).then([identifier]() { // TODO shutdown resource, or use the resource process with a --remove option to cleanup (so we can take advantage of the file locking) QDir dir(Sink::storageLocation()); for (const auto &folder : dir.entryList(QStringList() << identifier + "*")) { Sink::Storage(Sink::storageLocation(), folder, Sink::Storage::ReadWrite).removeFromDisk(); } }); } QPair, typename Sink::ResultEmitter::Ptr> ResourceFacade::load(const Sink::Query &query) { auto runner = new LocalStorageQueryRunner(query, mIdentifier, sConfigNotifier); auto monitoredResources = QSharedPointer>::create(); runner->setStatusUpdater([runner, monitoredResources](ApplicationDomain::SinkResource &resource) { auto resourceAccess = ResourceAccessFactory::instance().getAccess(resource.identifier(), ResourceConfig::getResourceType(resource.identifier())); if (!monitoredResources->contains(resource.identifier())) { //TODO disconnect at some point when the runner is done auto ret = QObject::connect(resourceAccess.data(), &ResourceAccess::notification, [resource, runner, resourceAccess](const Notification ¬ification) { Trace() << "Received notification in facade: " << notification.type; if (notification.type == Notification::Status) { runner->statusChanged(resource.identifier()); } }); Q_ASSERT(ret); monitoredResources->insert(resource.identifier()); } resource.setStatusStatus(resourceAccess->getResourceStatus()); }); return qMakePair(KAsync::null(), runner->emitter()); } AccountFacade::AccountFacade() : LocalStorageFacade("accounts") { } AccountFacade::~AccountFacade() { } IdentityFacade::IdentityFacade() : LocalStorageFacade("identities") { } IdentityFacade::~IdentityFacade() { } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundefined-reinterpret-cast" #include "moc_resourcefacade.cpp" #pragma clang diagnostic pop