summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2015-05-25 23:14:57 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2015-05-25 23:14:57 +0200
commit3601ee575f833bf204540f4fac41d87a0d977a79 (patch)
tree740e62969ba7cd6384161b40d499fd63b2029f5f
parent9e3bcbdd45ec05d0a1fd423e6219ac6443feed1c (diff)
downloadsink-3601ee575f833bf204540f4fac41d87a0d977a79.tar.gz
sink-3601ee575f833bf204540f4fac41d87a0d977a79.zip
Centralized type specific code.
-rw-r--r--common/CMakeLists.txt1
-rw-r--r--common/clientapi.h11
-rw-r--r--common/domain/event.cpp53
-rw-r--r--common/domain/event.h46
-rw-r--r--common/genericresource.cpp9
-rw-r--r--common/genericresource.h3
-rw-r--r--common/resultset.h61
-rw-r--r--examples/dummyresource/facade.cpp61
-rw-r--r--examples/dummyresource/resourcefactory.cpp28
9 files changed, 195 insertions, 78 deletions
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
25 threadboundary.cpp 25 threadboundary.cpp
26 messagequeue.cpp 26 messagequeue.cpp
27 index.cpp 27 index.cpp
28 domain/event.cpp
28 ${storage_SRCS}) 29 ${storage_SRCS})
29 30
30add_library(${PROJECT_NAME} SHARED ${command_SRCS}) 31add_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:
104 { 104 {
105 105
106 } 106 }
107 ApplicationDomainType(const QByteArray &resourceName, const QByteArray &identifier, qint64 revision, const QSharedPointer<BufferAdaptor> &adaptor) 107
108 ApplicationDomainType(const QByteArray &resourceInstanceIdentifier, const QByteArray &identifier, qint64 revision, const QSharedPointer<BufferAdaptor> &adaptor)
108 : mAdaptor(adaptor), 109 : mAdaptor(adaptor),
109 mResourceName(resourceName), 110 mResourceInstanceIdentifier(resourceInstanceIdentifier),
110 mIdentifier(identifier), 111 mIdentifier(identifier),
111 mRevision(revision) 112 mRevision(revision)
112 { 113 {
@@ -117,7 +118,7 @@ public:
117 { 118 {
118 //TODO only copy requested properties 119 //TODO only copy requested properties
119 auto memoryAdaptor = QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create(*(domainType->mAdaptor)); 120 auto memoryAdaptor = QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create(*(domainType->mAdaptor));
120 return QSharedPointer<DomainType>::create(domainType->mResourceName, domainType->mIdentifier, domainType->mRevision, memoryAdaptor); 121 return QSharedPointer<DomainType>::create(domainType->mResourceInstanceIdentifier, domainType->mIdentifier, domainType->mRevision, memoryAdaptor);
121 } 122 }
122 123
123 virtual ~ApplicationDomainType() {} 124 virtual ~ApplicationDomainType() {}
@@ -126,6 +127,8 @@ public:
126 virtual void setProperty(const QByteArray &key, const QVariant &value){ mChangeSet.insert(key, value); mAdaptor->setProperty(key, value); } 127 virtual void setProperty(const QByteArray &key, const QVariant &value){ mChangeSet.insert(key, value); mAdaptor->setProperty(key, value); }
127 virtual QByteArrayList changedProperties() const { return mChangeSet.keys(); } 128 virtual QByteArrayList changedProperties() const { return mChangeSet.keys(); }
128 qint64 revision() const { return mRevision; } 129 qint64 revision() const { return mRevision; }
130 QByteArray resourceInstanceIdentifier() const { return mResourceInstanceIdentifier; }
131 QByteArray identifier() const { return mIdentifier; }
129 132
130private: 133private:
131 QSharedPointer<BufferAdaptor> mAdaptor; 134 QSharedPointer<BufferAdaptor> mAdaptor;
@@ -133,7 +136,7 @@ private:
133 /* 136 /*
134 * Each domain object needs to store the resource, identifier, revision triple so we can link back to the storage location. 137 * Each domain object needs to store the resource, identifier, revision triple so we can link back to the storage location.
135 */ 138 */
136 QByteArray mResourceName; 139 QByteArray mResourceInstanceIdentifier;
137 QByteArray mIdentifier; 140 QByteArray mIdentifier;
138 qint64 mRevision; 141 qint64 mRevision;
139}; 142};
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 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19#include "event.h"
20
21#include <QVector>
22#include <QByteArray>
23
24#include "../resultset.h"
25#include "../index.h"
26#include "../storage.h"
27#include "../log.h"
28
29using namespace Akonadi2::ApplicationDomain;
30
31ResultSet EventImplementation::queryIndexes(const Akonadi2::Query &query, const QByteArray &resourceInstanceIdentifier)
32{
33 QVector<QByteArray> keys;
34 if (query.propertyFilter.contains("uid")) {
35 Index uidIndex(Akonadi2::Store::storageLocation(), resourceInstanceIdentifier + "index.uid", Akonadi2::Storage::ReadOnly);
36 uidIndex.lookup(query.propertyFilter.value("uid").toByteArray(), [&](const QByteArray &value) {
37 keys << value;
38 },
39 [](const Index::Error &error) {
40 Warning() << "Error in index: " << error.message;
41 });
42 }
43 return ResultSet(keys);
44}
45
46void EventImplementation::index(const Event &type)
47{
48 Index uidIndex(Akonadi2::Store::storageLocation(), type.resourceInstanceIdentifier() + "index.uid", Akonadi2::Storage::ReadWrite);
49 const auto uid = type.getProperty("uid");
50 if (uid.isValid()) {
51 uidIndex.add(uid.toByteArray(), type.identifier());
52 }
53}
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 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19#pragma once
20
21#include "../clientapi.h"
22
23class ResultSet;
24class QByteArray;
25
26namespace Akonadi2 {
27 class Query;
28
29namespace ApplicationDomain {
30
31/**
32 * Implements all type-specific code such as updating and querying indexes.
33 */
34namespace EventImplementation {
35 typedef Event DomainType;
36 /**
37 * Returns the potential result set based on the indexes.
38 *
39 * An empty result set indicates that a full scan is required.
40 */
41 ResultSet queryIndexes(const Akonadi2::Query &query, const QByteArray &resourceInstanceIdentifier);
42 void index(const Event &type);
43};
44
45}
46}
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:
153}; 153};
154 154
155 155
156GenericResource::GenericResource(const QByteArray &resourceIdentifier) 156GenericResource::GenericResource(const QByteArray &resourceInstanceIdentifier)
157 : Akonadi2::Resource(), 157 : Akonadi2::Resource(),
158 mUserQueue(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde." + resourceIdentifier + ".userqueue"), 158 mUserQueue(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde." + resourceInstanceIdentifier + ".userqueue"),
159 mSynchronizerQueue(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde." + resourceIdentifier + ".synchronizerqueue"), 159 mSynchronizerQueue(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde." + resourceInstanceIdentifier + ".synchronizerqueue"),
160 mError(0) 160 mError(0),
161 mResourceInstanceIdentifier(resourceInstanceIdentifier)
161{ 162{
162} 163}
163 164
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
34class AKONADI2COMMON_EXPORT GenericResource : public Resource 34class AKONADI2COMMON_EXPORT GenericResource : public Resource
35{ 35{
36public: 36public:
37 GenericResource(const QByteArray &resourceIdentifier); 37 GenericResource(const QByteArray &resourceInstanceIdentifier);
38 virtual ~GenericResource(); 38 virtual ~GenericResource();
39 39
40 virtual void processCommand(int commandId, const QByteArray &data, uint size, Pipeline *pipeline) Q_DECL_OVERRIDE; 40 virtual void processCommand(int commandId, const QByteArray &data, uint size, Pipeline *pipeline) Q_DECL_OVERRIDE;
@@ -50,6 +50,7 @@ protected:
50 flatbuffers::FlatBufferBuilder m_fbb; 50 flatbuffers::FlatBufferBuilder m_fbb;
51 MessageQueue mUserQueue; 51 MessageQueue mUserQueue;
52 MessageQueue mSynchronizerQueue; 52 MessageQueue mSynchronizerQueue;
53 QByteArray mResourceInstanceIdentifier;
53 54
54private: 55private:
55 Processor *mProcessor; 56 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 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19#pragma once
20
21#include <QVector>
22
23/*
24 * An iterator to a result set.
25 *
26 * We'll eventually want to lazy load results in next().
27 */
28class ResultSet {
29 public:
30 ResultSet(const QVector<QByteArray> &resultSet)
31 : mResultSet(resultSet),
32 mIt(nullptr)
33 {
34
35 }
36
37 bool next()
38 {
39 if (!mIt) {
40 mIt = mResultSet.constBegin();
41 } else {
42 mIt++;
43 }
44 return mIt != mResultSet.constEnd();
45 }
46
47 QByteArray id()
48 {
49 return *mIt;
50 }
51
52 bool isEmpty()
53 {
54 mResultSet.isEmpty();
55 }
56
57 private:
58 QVector<QByteArray> mResultSet;
59 QVector<QByteArray>::ConstIterator mIt;
60};
61
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 @@
24 24
25#include "common/resourceaccess.h" 25#include "common/resourceaccess.h"
26#include "common/commands.h" 26#include "common/commands.h"
27#include "common/resultset.h"
28#include "common/domain/event.h"
27#include "dummycalendar_generated.h" 29#include "dummycalendar_generated.h"
28#include "event_generated.h" 30#include "event_generated.h"
29#include "entity_generated.h" 31#include "entity_generated.h"
@@ -126,60 +128,16 @@ void DummyResourceFacade::readValue(const QSharedPointer<Akonadi2::Storage> &sto
126 }); 128 });
127} 129}
128 130
129/*
130 * An iterator to a result set.
131 *
132 * We'll eventually want to lazy load results in next().
133 */
134class ResultSet {
135 public:
136 ResultSet(const QVector<QByteArray> &resultSet)
137 : mResultSet(resultSet),
138 mIt(nullptr)
139 {
140
141 }
142
143 bool next()
144 {
145 if (!mIt) {
146 mIt = mResultSet.constBegin();
147 } else {
148 mIt++;
149 }
150 return mIt != mResultSet.constEnd();
151 }
152
153 QByteArray id()
154 {
155 return *mIt;
156 }
157
158 private:
159 QVector<QByteArray> mResultSet;
160 QVector<QByteArray>::ConstIterator mIt;
161};
162
163static ResultSet getResultSet(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::Storage> &storage) 131static ResultSet getResultSet(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::Storage> &storage)
164{ 132{
165 //Now that the sync is complete we can execute the query 133 auto resultSet = Akonadi2::ApplicationDomain::EventImplementation::queryIndexes(query, "org.kde.dummy");
166 const auto preparedQuery = prepareQuery(query);
167
168 //Index lookups
169 //TODO query standard indexes
170 QVector<QByteArray> keys;
171 if (query.propertyFilter.contains("uid")) {
172 static Index uidIndex(Akonadi2::Store::storageLocation(), "org.kde.dummy.index.uid", Akonadi2::Storage::ReadOnly);
173 uidIndex.lookup(query.propertyFilter.value("uid").toByteArray(), [&](const QByteArray &value) {
174 keys << value;
175 },
176 [](const Index::Error &error) {
177 Warning() << "Error in index: " << error.message;
178 });
179 }
180 134
181 //Scan for where we don't have an index 135 //Scan for where we don't have an index
182 if (keys.isEmpty()) { 136 //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
137 //TODO: the prepared query should be generalized in EventImplementation on top of domain adaptors
138 if (resultSet.isEmpty()) {
139 QVector<QByteArray> keys;
140 const auto preparedQuery = prepareQuery(query);
183 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) { 141 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) {
184 //TODO use adapter for query and scan? 142 //TODO use adapter for query and scan?
185 if (preparedQuery && preparedQuery(std::string(key.constData(), key.size()), buffer, local)) { 143 if (preparedQuery && preparedQuery(std::string(key.constData(), key.size()), buffer, local)) {
@@ -187,9 +145,10 @@ static ResultSet getResultSet(const Akonadi2::Query &query, const QSharedPointer
187 } 145 }
188 return true; 146 return true;
189 }); 147 });
148 return ResultSet(keys);
190 } 149 }
191 150
192 return ResultSet(keys); 151 return resultSet;
193} 152}
194 153
195KAsync::Job<qint64> DummyResourceFacade::load(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> > &resultProvider, qint64 oldRevision, qint64 newRevision) 154KAsync::Job<qint64> DummyResourceFacade::load(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> > &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 @@
30#include "clientapi.h" 30#include "clientapi.h"
31#include "index.h" 31#include "index.h"
32#include "log.h" 32#include "log.h"
33#include "domain/event.h"
33#include <QUuid> 34#include <QUuid>
34#include <assert.h> 35#include <assert.h>
35 36
@@ -106,35 +107,26 @@ static QMap<QString, QString> s_dataSource = populate();
106 107
107//FIXME We need to pass the resource-instance name to generic resource, not the plugin name 108//FIXME We need to pass the resource-instance name to generic resource, not the plugin name
108DummyResource::DummyResource() 109DummyResource::DummyResource()
109 : Akonadi2::GenericResource(PLUGIN_NAME) 110 : Akonadi2::GenericResource(PLUGIN_NAME ".instance1")
110{ 111{
111} 112}
112 113
113void DummyResource::configurePipeline(Akonadi2::Pipeline *pipeline) 114void DummyResource::configurePipeline(Akonadi2::Pipeline *pipeline)
114{ 115{
115 auto eventFactory = QSharedPointer<DummyEventAdaptorFactory>::create(); 116 //TODO In case of a non 1:1 mapping between resource and domain types special handling is required.
116 //FIXME we should setup for each resource entity type, not for each domain type
117 //i.e. If a resource stores tags as part of each message it needs to update the tag index 117 //i.e. If a resource stores tags as part of each message it needs to update the tag index
118 //TODO setup preprocessors for each resource entity type and pipeline type allowing full customization
119 //Eventually the order should be self configuring, for now it's hardcoded.
120 auto eventIndexer = new SimpleProcessor("summaryprocessor", [eventFactory](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity) {
121 auto adaptor = eventFactory->createAdaptor(entity);
122 // Log() << "Summary preprocessor: " << adaptor->getProperty("summary").toString();
123 });
124 118
125 auto uidIndexer = new SimpleProcessor("uidIndexer", [eventFactory](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity) { 119 auto eventFactory = QSharedPointer<DummyEventAdaptorFactory>::create();
126 static Index uidIndex(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde.dummy.index.uid", Akonadi2::Storage::ReadWrite); 120 const auto resourceIdentifier = mResourceInstanceIdentifier;
127 121 auto eventIndexer = new SimpleProcessor("eventIndexer", [eventFactory, resourceIdentifier](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity) {
128 //TODO: Benchmark if this is performance wise acceptable, or if we have to access the buffer directly
129 auto adaptor = eventFactory->createAdaptor(entity); 122 auto adaptor = eventFactory->createAdaptor(entity);
130 const auto uid = adaptor->getProperty("uid"); 123 //FIXME set revision?
131 if (uid.isValid()) { 124 Akonadi2::ApplicationDomain::Event event(resourceIdentifier, state.key(), -1, adaptor);
132 uidIndex.add(uid.toByteArray(), state.key()); 125 Akonadi2::ApplicationDomain::EventImplementation::index(event);
133 }
134 }); 126 });
135 127
136 //event is the entitytype and not the domain type 128 //event is the entitytype and not the domain type
137 pipeline->setPreprocessors("event", Akonadi2::Pipeline::NewPipeline, QVector<Akonadi2::Preprocessor*>() << eventIndexer << uidIndexer); 129 pipeline->setPreprocessors("event", Akonadi2::Pipeline::NewPipeline, QVector<Akonadi2::Preprocessor*>() << eventIndexer);
138 GenericResource::configurePipeline(pipeline); 130 GenericResource::configurePipeline(pipeline);
139} 131}
140 132