/* * Copyright (C) 2014 Christian Mollekopf * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * 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, see . */ #include "clientapi.h" #include #include #include #include #include #include #include #include "resourceaccess.h" #include "commands.h" #include "resourcefacade.h" #include "definitions.h" #include "resourceconfig.h" #include "facadefactory.h" #include "modelresult.h" #include "storage.h" #include "log.h" namespace Akonadi2 { QString Store::storageLocation() { return Akonadi2::storageLocation(); } QByteArray Store::resourceName(const QByteArray &instanceIdentifier) { return Akonadi2::resourceName(instanceIdentifier); } QList Store::getResources(const QList &resourceFilter, const QByteArray &type) { //Return the global resource (signified by an empty name) for types that don't eblong to a specific resource if (type == "akonadiresource") { qWarning() << "Global resource"; return QList() << ""; } QList resources; const auto configuredResources = ResourceConfig::getResources(); if (resourceFilter.isEmpty()) { for (const auto &res : configuredResources.keys()) { //TODO filter by entity type resources << res; } } else { for (const auto &res : resourceFilter) { if (configuredResources.contains(res)) { resources << res; } else { qWarning() << "Resource is not existing: " << res; } } } qWarning() << "Found resources: " << resources; return resources; } template QSharedPointer Store::loadModel(Query query) { auto model = QSharedPointer >::create(query, query.requestedProperties); //* Client defines lifetime of model //* The model lifetime defines the duration of live-queries //* The facade needs to life for the duration of any calls being made (assuming we get rid of any internal callbacks //* The emitter needs to live or the duration of query (respectively, the model) //* The result provider needs to live for as long as results are provided (until the last thread exits). // Query all resources and aggregate results KAsync::iterate(getResources(query.resources, ApplicationDomain::getTypeName())) .template each([query, model](const QByteArray &resource, KAsync::Future &future) { auto facade = FacadeFactory::instance().getFacade(resourceName(resource), resource); if (facade) { Trace() << "Trying to fetch from resource"; auto result = facade->load(query); auto emitter = result.second; //TODO use aggregating emitter instead model->setEmitter(emitter); model->fetchMore(QModelIndex()); result.first.template then([&future](){future.setFinished();}).exec(); } else { //Ignore the error and carry on future.setFinished(); } }).exec(); return model; } template static std::shared_ptr > getFacade(const QByteArray &resourceInstanceIdentifier) { if (auto facade = FacadeFactory::instance().getFacade(resourceName(resourceInstanceIdentifier), resourceInstanceIdentifier)) { return facade; } return std::make_shared >(); } template KAsync::Job Store::create(const DomainType &domainObject) { //Potentially move to separate thread as well auto facade = getFacade(domainObject.resourceInstanceIdentifier()); return facade->create(domainObject).template then([facade](){}, [](int errorCode, const QString &error) { Warning() << "Failed to create"; }); } template KAsync::Job Store::modify(const DomainType &domainObject) { //Potentially move to separate thread as well auto facade = getFacade(domainObject.resourceInstanceIdentifier()); return facade->modify(domainObject).template then([facade](){}, [](int errorCode, const QString &error) { Warning() << "Failed to modify"; }); } template KAsync::Job Store::remove(const DomainType &domainObject) { //Potentially move to separate thread as well auto facade = getFacade(domainObject.resourceInstanceIdentifier()); return facade->remove(domainObject).template then([facade](){}, [](int errorCode, const QString &error) { Warning() << "Failed to remove"; }); } KAsync::Job Store::shutdown(const QByteArray &identifier) { Trace() << "shutdown " << identifier; return ResourceAccess::connectToServer(identifier).then>([identifier](QSharedPointer socket, KAsync::Future &future) { //We can't currently reuse the socket socket->close(); auto resourceAccess = QSharedPointer::create(identifier); resourceAccess->open(); resourceAccess->sendCommand(Akonadi2::Commands::ShutdownCommand).then([&future, resourceAccess]() { Trace() << "Shutdown complete"; future.setFinished(); }).exec(); }, [](int, const QString &) { Trace() << "Resource is already closed."; //Resource isn't started, nothing to shutdown }) //FIXME JOBAPI this is only required because we don't care about the return value of connectToServer .template then([](){}); } KAsync::Job Store::start(const QByteArray &identifier) { Trace() << "start " << identifier; auto resourceAccess = QSharedPointer::create(identifier); resourceAccess->open(); return resourceAccess->sendCommand(Akonadi2::Commands::PingCommand).then([resourceAccess]() { Trace() << "Start complete"; }); } void Store::removeFromDisk(const QByteArray &identifier) { //TODO By calling the resource executable with a --remove option instead //we can ensure that no other resource process is running at the same time QDir dir(Akonadi2::storageLocation()); for (const auto &folder : dir.entryList(QStringList() << identifier + "*")) { Akonadi2::Storage(Akonadi2::storageLocation(), folder, Akonadi2::Storage::ReadWrite).removeFromDisk(); } } KAsync::Job Store::synchronize(const Akonadi2::Query &query) { Trace() << "synchronize"; return KAsync::iterate(query.resources) .template each([query](const QByteArray &resource, KAsync::Future &future) { auto resourceAccess = QSharedPointer::create(resource); resourceAccess->open(); resourceAccess->synchronizeResource(query.syncOnDemand, query.processAll).then([&future, resourceAccess]() { future.setFinished(); }).exec(); }) //FIXME JOBAPI this is only required because we don't care about the return value of each (and each shouldn't even have a return value) .template then([](){}); } #define REGISTER_TYPE(T) template KAsync::Job Store::remove(const T &domainObject); \ template KAsync::Job Store::create(const T &domainObject); \ template KAsync::Job Store::modify(const T &domainObject); \ template QSharedPointer Store::loadModel(Query query); \ REGISTER_TYPE(ApplicationDomain::Event); REGISTER_TYPE(ApplicationDomain::Mail); REGISTER_TYPE(ApplicationDomain::Folder); REGISTER_TYPE(ApplicationDomain::AkonadiResource); } // namespace Akonadi2