From 3601ee575f833bf204540f4fac41d87a0d977a79 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Mon, 25 May 2015 23:14:57 +0200 Subject: Centralized type specific code. --- common/CMakeLists.txt | 1 + common/clientapi.h | 11 ++++-- common/domain/event.cpp | 53 ++++++++++++++++++++++++++ common/domain/event.h | 46 ++++++++++++++++++++++ common/genericresource.cpp | 9 +++-- common/genericresource.h | 3 +- common/resultset.h | 61 ++++++++++++++++++++++++++++++ examples/dummyresource/facade.cpp | 61 +++++------------------------- examples/dummyresource/resourcefactory.cpp | 28 +++++--------- 9 files changed, 195 insertions(+), 78 deletions(-) create mode 100644 common/domain/event.cpp create mode 100644 common/domain/event.h create mode 100644 common/resultset.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 2ece210..37b5b3f 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -25,6 +25,7 @@ set(command_SRCS threadboundary.cpp messagequeue.cpp index.cpp + domain/event.cpp ${storage_SRCS}) add_library(${PROJECT_NAME} SHARED ${command_SRCS}) diff --git a/common/clientapi.h b/common/clientapi.h index ee4ef3f..d26a2ad 100644 --- a/common/clientapi.h +++ b/common/clientapi.h @@ -104,9 +104,10 @@ public: { } - ApplicationDomainType(const QByteArray &resourceName, const QByteArray &identifier, qint64 revision, const QSharedPointer &adaptor) + + ApplicationDomainType(const QByteArray &resourceInstanceIdentifier, const QByteArray &identifier, qint64 revision, const QSharedPointer &adaptor) : mAdaptor(adaptor), - mResourceName(resourceName), + mResourceInstanceIdentifier(resourceInstanceIdentifier), mIdentifier(identifier), mRevision(revision) { @@ -117,7 +118,7 @@ public: { //TODO only copy requested properties auto memoryAdaptor = QSharedPointer::create(*(domainType->mAdaptor)); - return QSharedPointer::create(domainType->mResourceName, domainType->mIdentifier, domainType->mRevision, memoryAdaptor); + return QSharedPointer::create(domainType->mResourceInstanceIdentifier, domainType->mIdentifier, domainType->mRevision, memoryAdaptor); } virtual ~ApplicationDomainType() {} @@ -126,6 +127,8 @@ public: virtual void setProperty(const QByteArray &key, const QVariant &value){ mChangeSet.insert(key, value); mAdaptor->setProperty(key, value); } virtual QByteArrayList changedProperties() const { return mChangeSet.keys(); } qint64 revision() const { return mRevision; } + QByteArray resourceInstanceIdentifier() const { return mResourceInstanceIdentifier; } + QByteArray identifier() const { return mIdentifier; } private: QSharedPointer mAdaptor; @@ -133,7 +136,7 @@ private: /* * Each domain object needs to store the resource, identifier, revision triple so we can link back to the storage location. */ - QByteArray mResourceName; + QByteArray mResourceInstanceIdentifier; QByteArray mIdentifier; qint64 mRevision; }; diff --git a/common/domain/event.cpp b/common/domain/event.cpp new file mode 100644 index 0000000..86100b7 --- /dev/null +++ b/common/domain/event.cpp @@ -0,0 +1,53 @@ +/* + * 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 "event.h" + +#include +#include + +#include "../resultset.h" +#include "../index.h" +#include "../storage.h" +#include "../log.h" + +using namespace Akonadi2::ApplicationDomain; + +ResultSet EventImplementation::queryIndexes(const Akonadi2::Query &query, const QByteArray &resourceInstanceIdentifier) +{ + QVector keys; + if (query.propertyFilter.contains("uid")) { + Index uidIndex(Akonadi2::Store::storageLocation(), resourceInstanceIdentifier + "index.uid", Akonadi2::Storage::ReadOnly); + uidIndex.lookup(query.propertyFilter.value("uid").toByteArray(), [&](const QByteArray &value) { + keys << value; + }, + [](const Index::Error &error) { + Warning() << "Error in index: " << error.message; + }); + } + return ResultSet(keys); +} + +void EventImplementation::index(const Event &type) +{ + Index uidIndex(Akonadi2::Store::storageLocation(), type.resourceInstanceIdentifier() + "index.uid", Akonadi2::Storage::ReadWrite); + const auto uid = type.getProperty("uid"); + if (uid.isValid()) { + uidIndex.add(uid.toByteArray(), type.identifier()); + } +} diff --git a/common/domain/event.h b/common/domain/event.h new file mode 100644 index 0000000..4cb0d34 --- /dev/null +++ b/common/domain/event.h @@ -0,0 +1,46 @@ +/* + * 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. + */ +#pragma once + +#include "../clientapi.h" + +class ResultSet; +class QByteArray; + +namespace Akonadi2 { + class Query; + +namespace ApplicationDomain { + +/** + * Implements all type-specific code such as updating and querying indexes. + */ +namespace EventImplementation { + typedef Event DomainType; + /** + * Returns the potential result set based on the indexes. + * + * An empty result set indicates that a full scan is required. + */ + ResultSet queryIndexes(const Akonadi2::Query &query, const QByteArray &resourceInstanceIdentifier); + void index(const Event &type); +}; + +} +} diff --git a/common/genericresource.cpp b/common/genericresource.cpp index 4467e86..fdc8b14 100644 --- a/common/genericresource.cpp +++ b/common/genericresource.cpp @@ -153,11 +153,12 @@ private: }; -GenericResource::GenericResource(const QByteArray &resourceIdentifier) +GenericResource::GenericResource(const QByteArray &resourceInstanceIdentifier) : Akonadi2::Resource(), - mUserQueue(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde." + resourceIdentifier + ".userqueue"), - mSynchronizerQueue(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde." + resourceIdentifier + ".synchronizerqueue"), - mError(0) + mUserQueue(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde." + resourceInstanceIdentifier + ".userqueue"), + mSynchronizerQueue(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde." + resourceInstanceIdentifier + ".synchronizerqueue"), + mError(0), + mResourceInstanceIdentifier(resourceInstanceIdentifier) { } diff --git a/common/genericresource.h b/common/genericresource.h index ac28575..c44989e 100644 --- a/common/genericresource.h +++ b/common/genericresource.h @@ -34,7 +34,7 @@ namespace Akonadi2 class AKONADI2COMMON_EXPORT GenericResource : public Resource { public: - GenericResource(const QByteArray &resourceIdentifier); + GenericResource(const QByteArray &resourceInstanceIdentifier); virtual ~GenericResource(); virtual void processCommand(int commandId, const QByteArray &data, uint size, Pipeline *pipeline) Q_DECL_OVERRIDE; @@ -50,6 +50,7 @@ protected: flatbuffers::FlatBufferBuilder m_fbb; MessageQueue mUserQueue; MessageQueue mSynchronizerQueue; + QByteArray mResourceInstanceIdentifier; private: Processor *mProcessor; diff --git a/common/resultset.h b/common/resultset.h new file mode 100644 index 0000000..7d7f19a --- /dev/null +++ b/common/resultset.h @@ -0,0 +1,61 @@ +/* + * 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. + */ +#pragma once + +#include + +/* + * An iterator to a result set. + * + * We'll eventually want to lazy load results in next(). + */ +class ResultSet { + public: + ResultSet(const QVector &resultSet) + : mResultSet(resultSet), + mIt(nullptr) + { + + } + + bool next() + { + if (!mIt) { + mIt = mResultSet.constBegin(); + } else { + mIt++; + } + return mIt != mResultSet.constEnd(); + } + + QByteArray id() + { + return *mIt; + } + + bool isEmpty() + { + mResultSet.isEmpty(); + } + + private: + QVector mResultSet; + QVector::ConstIterator mIt; +}; + diff --git a/examples/dummyresource/facade.cpp b/examples/dummyresource/facade.cpp index 9722335..611217f 100644 --- a/examples/dummyresource/facade.cpp +++ b/examples/dummyresource/facade.cpp @@ -24,6 +24,8 @@ #include "common/resourceaccess.h" #include "common/commands.h" +#include "common/resultset.h" +#include "common/domain/event.h" #include "dummycalendar_generated.h" #include "event_generated.h" #include "entity_generated.h" @@ -126,60 +128,16 @@ void DummyResourceFacade::readValue(const QSharedPointer &sto }); } -/* - * An iterator to a result set. - * - * We'll eventually want to lazy load results in next(). - */ -class ResultSet { - public: - ResultSet(const QVector &resultSet) - : mResultSet(resultSet), - mIt(nullptr) - { - - } - - bool next() - { - if (!mIt) { - mIt = mResultSet.constBegin(); - } else { - mIt++; - } - return mIt != mResultSet.constEnd(); - } - - QByteArray id() - { - return *mIt; - } - - private: - QVector mResultSet; - QVector::ConstIterator mIt; -}; - static ResultSet getResultSet(const Akonadi2::Query &query, const QSharedPointer &storage) { - //Now that the sync is complete we can execute the query - const auto preparedQuery = prepareQuery(query); - - //Index lookups - //TODO query standard indexes - QVector keys; - if (query.propertyFilter.contains("uid")) { - static Index uidIndex(Akonadi2::Store::storageLocation(), "org.kde.dummy.index.uid", Akonadi2::Storage::ReadOnly); - uidIndex.lookup(query.propertyFilter.value("uid").toByteArray(), [&](const QByteArray &value) { - keys << value; - }, - [](const Index::Error &error) { - Warning() << "Error in index: " << error.message; - }); - } + auto resultSet = Akonadi2::ApplicationDomain::EventImplementation::queryIndexes(query, "org.kde.dummy"); //Scan for where we don't have an index - if (keys.isEmpty()) { + //TODO: we may want a way for queryIndexes to indicate that the resultSet is not final, and that a scan over the remaining set is required + //TODO: the prepared query should be generalized in EventImplementation on top of domain adaptors + if (resultSet.isEmpty()) { + QVector keys; + const auto preparedQuery = prepareQuery(query); scan(storage, QByteArray(), [preparedQuery, &keys](const QByteArray &key, const Akonadi2::Entity &entity, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local, Akonadi2::Metadata const *metadataBuffer) { //TODO use adapter for query and scan? if (preparedQuery && preparedQuery(std::string(key.constData(), key.size()), buffer, local)) { @@ -187,9 +145,10 @@ static ResultSet getResultSet(const Akonadi2::Query &query, const QSharedPointer } return true; }); + return ResultSet(keys); } - return ResultSet(keys); + return resultSet; } KAsync::Job DummyResourceFacade::load(const Akonadi2::Query &query, const QSharedPointer > &resultProvider, qint64 oldRevision, qint64 newRevision) diff --git a/examples/dummyresource/resourcefactory.cpp b/examples/dummyresource/resourcefactory.cpp index e16a693..71a4ac5 100644 --- a/examples/dummyresource/resourcefactory.cpp +++ b/examples/dummyresource/resourcefactory.cpp @@ -30,6 +30,7 @@ #include "clientapi.h" #include "index.h" #include "log.h" +#include "domain/event.h" #include #include @@ -106,35 +107,26 @@ static QMap s_dataSource = populate(); //FIXME We need to pass the resource-instance name to generic resource, not the plugin name DummyResource::DummyResource() - : Akonadi2::GenericResource(PLUGIN_NAME) + : Akonadi2::GenericResource(PLUGIN_NAME ".instance1") { } void DummyResource::configurePipeline(Akonadi2::Pipeline *pipeline) { - auto eventFactory = QSharedPointer::create(); - //FIXME we should setup for each resource entity type, not for each domain type + //TODO In case of a non 1:1 mapping between resource and domain types special handling is required. //i.e. If a resource stores tags as part of each message it needs to update the tag index - //TODO setup preprocessors for each resource entity type and pipeline type allowing full customization - //Eventually the order should be self configuring, for now it's hardcoded. - auto eventIndexer = new SimpleProcessor("summaryprocessor", [eventFactory](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity) { - auto adaptor = eventFactory->createAdaptor(entity); - // Log() << "Summary preprocessor: " << adaptor->getProperty("summary").toString(); - }); - auto uidIndexer = new SimpleProcessor("uidIndexer", [eventFactory](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity) { - static Index uidIndex(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde.dummy.index.uid", Akonadi2::Storage::ReadWrite); - - //TODO: Benchmark if this is performance wise acceptable, or if we have to access the buffer directly + auto eventFactory = QSharedPointer::create(); + const auto resourceIdentifier = mResourceInstanceIdentifier; + auto eventIndexer = new SimpleProcessor("eventIndexer", [eventFactory, resourceIdentifier](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity) { auto adaptor = eventFactory->createAdaptor(entity); - const auto uid = adaptor->getProperty("uid"); - if (uid.isValid()) { - uidIndex.add(uid.toByteArray(), state.key()); - } + //FIXME set revision? + Akonadi2::ApplicationDomain::Event event(resourceIdentifier, state.key(), -1, adaptor); + Akonadi2::ApplicationDomain::EventImplementation::index(event); }); //event is the entitytype and not the domain type - pipeline->setPreprocessors("event", Akonadi2::Pipeline::NewPipeline, QVector() << eventIndexer << uidIndexer); + pipeline->setPreprocessors("event", Akonadi2::Pipeline::NewPipeline, QVector() << eventIndexer); GenericResource::configurePipeline(pipeline); } -- cgit v1.2.3