summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/CMakeLists.txt2
-rw-r--r--common/domain/applicationdomaintype.cpp12
-rw-r--r--common/domain/applicationdomaintype.h14
-rw-r--r--common/domain/mail.cpp104
-rw-r--r--common/domain/mail.fbs15
-rw-r--r--common/domain/mail.h60
-rw-r--r--common/domainadaptor.h1
-rw-r--r--common/entitystorage.cpp12
-rw-r--r--common/entitystorage.h7
-rw-r--r--common/facade.h2
-rw-r--r--common/pipeline.cpp64
-rw-r--r--common/pipeline.h7
-rw-r--r--common/propertymapper.cpp6
-rw-r--r--common/propertymapper.h2
-rw-r--r--common/storage.h2
-rw-r--r--common/storage_common.cpp12
-rw-r--r--examples/dummyresource/domainadaptor.cpp6
-rw-r--r--examples/dummyresource/domainadaptor.h9
-rw-r--r--examples/dummyresource/dummystore.cpp33
-rw-r--r--examples/dummyresource/dummystore.h4
-rw-r--r--examples/dummyresource/facade.cpp31
-rw-r--r--examples/dummyresource/facade.h18
-rw-r--r--examples/dummyresource/resourcefactory.cpp130
-rw-r--r--examples/dummyresource/resourcefactory.h3
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/dummyresourcetest.cpp15
-rw-r--r--tests/genericfacadebenchmark.cpp111
-rw-r--r--tests/genericfacadetest.cpp4
28 files changed, 605 insertions, 82 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 2f779b5..25ea667 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -34,6 +34,7 @@ set(command_SRCS
34 resourceconfig.cpp 34 resourceconfig.cpp
35 domain/applicationdomaintype.cpp 35 domain/applicationdomaintype.cpp
36 domain/event.cpp 36 domain/event.cpp
37 domain/mail.cpp
37 ${storage_SRCS}) 38 ${storage_SRCS})
38 39
39add_library(${PROJECT_NAME} SHARED ${command_SRCS}) 40add_library(${PROJECT_NAME} SHARED ${command_SRCS})
@@ -51,6 +52,7 @@ generate_flatbuffers(
51 commands/synchronize 52 commands/synchronize
52 commands/notification 53 commands/notification
53 domain/event 54 domain/event
55 domain/mail
54 entity 56 entity
55 metadata 57 metadata
56 queuedcommand 58 queuedcommand
diff --git a/common/domain/applicationdomaintype.cpp b/common/domain/applicationdomaintype.cpp
index 47ff0c3..3cc075b 100644
--- a/common/domain/applicationdomaintype.cpp
+++ b/common/domain/applicationdomaintype.cpp
@@ -40,6 +40,18 @@ QByteArray getTypeName<AkonadiResource>()
40 return "akonadiresource"; 40 return "akonadiresource";
41} 41}
42 42
43template<>
44QByteArray getTypeName<Mail>()
45{
46 return "mail";
47}
48
49template<>
50QByteArray getTypeName<Folder>()
51{
52 return "folder";
53}
54
43} 55}
44} 56}
45 57
diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h
index 29bebcf..e0a6de0 100644
--- a/common/domain/applicationdomaintype.h
+++ b/common/domain/applicationdomaintype.h
@@ -112,10 +112,14 @@ struct Calendar : public ApplicationDomainType {
112 using ApplicationDomainType::ApplicationDomainType; 112 using ApplicationDomainType::ApplicationDomainType;
113}; 113};
114 114
115class Mail : public ApplicationDomainType { 115struct Mail : public ApplicationDomainType {
116 typedef QSharedPointer<Mail> Ptr;
117 using ApplicationDomainType::ApplicationDomainType;
116}; 118};
117 119
118class Folder : public ApplicationDomainType { 120struct Folder : public ApplicationDomainType {
121 typedef QSharedPointer<Folder> Ptr;
122 using ApplicationDomainType::ApplicationDomainType;
119}; 123};
120 124
121/** 125/**
@@ -146,6 +150,12 @@ QByteArray getTypeName<Todo>();
146template<> 150template<>
147QByteArray getTypeName<AkonadiResource>(); 151QByteArray getTypeName<AkonadiResource>();
148 152
153template<>
154QByteArray getTypeName<Mail>();
155
156template<>
157QByteArray getTypeName<Folder>();
158
149/** 159/**
150 * Type implementation. 160 * Type implementation.
151 * 161 *
diff --git a/common/domain/mail.cpp b/common/domain/mail.cpp
new file mode 100644
index 0000000..230ef31
--- /dev/null
+++ b/common/domain/mail.cpp
@@ -0,0 +1,104 @@
1/*
2 * Copyright (C) 2015 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 "mail.h"
20
21#include <QVector>
22#include <QByteArray>
23#include <QString>
24
25#include "../resultset.h"
26#include "../index.h"
27#include "../storage.h"
28#include "../log.h"
29#include "../propertymapper.h"
30#include "../query.h"
31#include "../definitions.h"
32
33#include "mail_generated.h"
34
35using namespace Akonadi2::ApplicationDomain;
36
37ResultSet TypeImplementation<Mail>::queryIndexes(const Akonadi2::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Akonadi2::Storage::Transaction &transaction)
38{
39 QVector<QByteArray> keys;
40 if (query.propertyFilter.contains("uid")) {
41 Index uidIndex("mail.index.uid", transaction);
42 uidIndex.lookup(query.propertyFilter.value("uid").toByteArray(), [&](const QByteArray &value) {
43 keys << value;
44 },
45 [](const Index::Error &error) {
46 Warning() << "Error in uid index: " << error.message;
47 });
48 appliedFilters << "uid";
49 }
50 return ResultSet(keys);
51}
52
53void TypeImplementation<Mail>::index(const Mail &type, Akonadi2::Storage::Transaction &transaction)
54{
55 const auto uid = type.getProperty("uid");
56 if (uid.isValid()) {
57 Index uidIndex("mail.index.uid", transaction);
58 uidIndex.add(uid.toByteArray(), type.identifier());
59 }
60}
61
62QSharedPointer<ReadPropertyMapper<TypeImplementation<Mail>::Buffer> > TypeImplementation<Mail>::initializeReadPropertyMapper()
63{
64 auto propertyMapper = QSharedPointer<ReadPropertyMapper<Buffer> >::create();
65 propertyMapper->addMapping("uid", [](Buffer const *buffer) -> QVariant {
66 return propertyToVariant<QString>(buffer->uid());
67 });
68 propertyMapper->addMapping("sender", [](Buffer const *buffer) -> QVariant {
69 return propertyToVariant<QString>(buffer->sender());
70 });
71 propertyMapper->addMapping("senderName", [](Buffer const *buffer) -> QVariant {
72 return propertyToVariant<QString>(buffer->senderName());
73 });
74 propertyMapper->addMapping("subject", [](Buffer const *buffer) -> QVariant {
75 return propertyToVariant<QString>(buffer->subject());
76 });
77 propertyMapper->addMapping("date", [](Buffer const *buffer) -> QVariant {
78 return propertyToVariant<QString>(buffer->date());
79 });
80 propertyMapper->addMapping("unread", [](Buffer const *buffer) -> QVariant {
81 return propertyToVariant<bool>(buffer->unread());
82 });
83 propertyMapper->addMapping("important", [](Buffer const *buffer) -> QVariant {
84 return propertyToVariant<bool>(buffer->important());
85 });
86 propertyMapper->addMapping("folder", [](Buffer const *buffer) -> QVariant {
87 return propertyToVariant<QString>(buffer->folder());
88 });
89 return propertyMapper;
90}
91
92QSharedPointer<WritePropertyMapper<TypeImplementation<Mail>::BufferBuilder> > TypeImplementation<Mail>::initializeWritePropertyMapper()
93{
94 auto propertyMapper = QSharedPointer<WritePropertyMapper<BufferBuilder> >::create();
95 // propertyMapper->addMapping("summary", [](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> {
96 // auto offset = variantToProperty<QString>(value, fbb);
97 // return [offset](BufferBuilder &builder) { builder.add_summary(offset); };
98 // });
99 propertyMapper->addMapping("uid", [](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(BufferBuilder &)> {
100 auto offset = variantToProperty<QString>(value, fbb);
101 return [offset](BufferBuilder &builder) { builder.add_uid(offset); };
102 });
103 return propertyMapper;
104}
diff --git a/common/domain/mail.fbs b/common/domain/mail.fbs
new file mode 100644
index 0000000..13aa36d
--- /dev/null
+++ b/common/domain/mail.fbs
@@ -0,0 +1,15 @@
1namespace Akonadi2.ApplicationDomain.Buffer;
2
3table Mail {
4 uid:string;
5 folder:string;
6 sender:string;
7 senderName:string;
8 subject:string;
9 date:string;
10 unread:bool = false;
11 important:bool = false;
12}
13
14root_type Mail;
15file_identifier "AKFB";
diff --git a/common/domain/mail.h b/common/domain/mail.h
new file mode 100644
index 0000000..b58ce44
--- /dev/null
+++ b/common/domain/mail.h
@@ -0,0 +1,60 @@
1/*
2 * Copyright (C) 2015 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 "applicationdomaintype.h"
22
23#include "storage.h"
24
25class ResultSet;
26class QByteArray;
27
28template<typename T>
29class ReadPropertyMapper;
30template<typename T>
31class WritePropertyMapper;
32
33namespace Akonadi2 {
34 class Query;
35
36namespace ApplicationDomain {
37 namespace Buffer {
38 struct Mail;
39 struct MailBuilder;
40 }
41
42template<>
43class TypeImplementation<Akonadi2::ApplicationDomain::Mail> {
44public:
45 typedef Akonadi2::ApplicationDomain::Buffer::Mail Buffer;
46 typedef Akonadi2::ApplicationDomain::Buffer::MailBuilder BufferBuilder;
47 static QSet<QByteArray> indexedProperties();
48 /**
49 * Returns the potential result set based on the indexes.
50 *
51 * An empty result set indicates that a full scan is required.
52 */
53 static ResultSet queryIndexes(const Akonadi2::Query &query, const QByteArray &resourceInstanceIdentifier, QSet<QByteArray> &appliedFilters, Akonadi2::Storage::Transaction &transaction);
54 static void index(const Mail &type, Akonadi2::Storage::Transaction &transaction);
55 static QSharedPointer<ReadPropertyMapper<Buffer> > initializeReadPropertyMapper();
56 static QSharedPointer<WritePropertyMapper<BufferBuilder> > initializeWritePropertyMapper();
57};
58
59}
60}
diff --git a/common/domainadaptor.h b/common/domainadaptor.h
index f9dcc79..4943cc0 100644
--- a/common/domainadaptor.h
+++ b/common/domainadaptor.h
@@ -25,6 +25,7 @@
25 25
26#include "domain/applicationdomaintype.h" 26#include "domain/applicationdomaintype.h"
27#include "domain/event.h" 27#include "domain/event.h"
28#include "domain/mail.h"
28#include "entity_generated.h" 29#include "entity_generated.h"
29#include "metadata_generated.h" 30#include "metadata_generated.h"
30#include "entitybuffer.h" 31#include "entitybuffer.h"
diff --git a/common/entitystorage.cpp b/common/entitystorage.cpp
index 8a3391e..bcc3562 100644
--- a/common/entitystorage.cpp
+++ b/common/entitystorage.cpp
@@ -19,9 +19,9 @@
19 19
20#include "entitystorage.h" 20#include "entitystorage.h"
21 21
22static void scan(const Akonadi2::Storage::Transaction &transaction, const QByteArray &key, std::function<bool(const QByteArray &key, const Akonadi2::Entity &entity)> callback) 22static void scan(const Akonadi2::Storage::Transaction &transaction, const QByteArray &key, std::function<bool(const QByteArray &key, const Akonadi2::Entity &entity)> callback, const QByteArray &bufferType)
23{ 23{
24 transaction.openDatabase().scan(key, [=](const QByteArray &key, const QByteArray &value) -> bool { 24 transaction.openDatabase(bufferType + ".main").scan(key, [=](const QByteArray &key, const QByteArray &value) -> bool {
25 //Skip internals 25 //Skip internals
26 if (Akonadi2::Storage::isInternalKey(key)) { 26 if (Akonadi2::Storage::isInternalKey(key)) {
27 return true; 27 return true;
@@ -58,17 +58,17 @@ void EntityStorageBase::readValue(const Akonadi2::Storage::Transaction &transact
58 auto domainObject = create(key, revision, mDomainTypeAdaptorFactory->createAdaptor(entity)); 58 auto domainObject = create(key, revision, mDomainTypeAdaptorFactory->createAdaptor(entity));
59 resultCallback(domainObject); 59 resultCallback(domainObject);
60 return true; 60 return true;
61 }); 61 }, mBufferType);
62} 62}
63 63
64static ResultSet fullScan(const Akonadi2::Storage::Transaction &transaction) 64static ResultSet fullScan(const Akonadi2::Storage::Transaction &transaction, const QByteArray &bufferType)
65{ 65{
66 //TODO use a result set with an iterator, to read values on demand 66 //TODO use a result set with an iterator, to read values on demand
67 QVector<QByteArray> keys; 67 QVector<QByteArray> keys;
68 scan(transaction, QByteArray(), [=, &keys](const QByteArray &key, const Akonadi2::Entity &) { 68 scan(transaction, QByteArray(), [=, &keys](const QByteArray &key, const Akonadi2::Entity &) {
69 keys << key; 69 keys << key;
70 return true; 70 return true;
71 }); 71 }, bufferType);
72 Trace() << "Full scan found " << keys.size() << " results"; 72 Trace() << "Full scan found " << keys.size() << " results";
73 return ResultSet(keys); 73 return ResultSet(keys);
74} 74}
@@ -99,7 +99,7 @@ ResultSet EntityStorageBase::getResultSet(const Akonadi2::Query &query, Akonadi2
99 99
100 //We do a full scan if there were no indexes available to create the initial set. 100 //We do a full scan if there were no indexes available to create the initial set.
101 if (appliedFilters.isEmpty()) { 101 if (appliedFilters.isEmpty()) {
102 resultSet = fullScan(transaction); 102 resultSet = fullScan(transaction, mBufferType);
103 } 103 }
104 104
105 auto filter = [remainingFilters, query, baseRevision, topRevision](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject) -> bool { 105 auto filter = [remainingFilters, query, baseRevision, topRevision](const Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr &domainObject) -> bool {
diff --git a/common/entitystorage.h b/common/entitystorage.h
index 8256938..9d928b8 100644
--- a/common/entitystorage.h
+++ b/common/entitystorage.h
@@ -52,6 +52,7 @@ protected:
52 52
53protected: 53protected:
54 QByteArray mResourceInstanceIdentifier; 54 QByteArray mResourceInstanceIdentifier;
55 QByteArray mBufferType;
55 DomainTypeAdaptorFactoryInterface::Ptr mDomainTypeAdaptorFactory; 56 DomainTypeAdaptorFactoryInterface::Ptr mDomainTypeAdaptorFactory;
56}; 57};
57 58
@@ -60,10 +61,10 @@ class EntityStorage : public EntityStorageBase
60{ 61{
61 62
62public: 63public:
63 EntityStorage(const QByteArray &instanceIdentifier, const DomainTypeAdaptorFactoryInterface::Ptr &adaptorFactory) 64 EntityStorage(const QByteArray &instanceIdentifier, const DomainTypeAdaptorFactoryInterface::Ptr &adaptorFactory, const QByteArray &bufferType)
64 : EntityStorageBase(instanceIdentifier, adaptorFactory) 65 : EntityStorageBase(instanceIdentifier, adaptorFactory)
65 { 66 {
66 67 mBufferType = bufferType;
67 } 68 }
68 69
69protected: 70protected:
@@ -84,7 +85,7 @@ protected:
84 85
85public: 86public:
86 87
87 virtual void read(const Akonadi2::Query &query, const QPair<qint64, qint64> &revisionRange, const QSharedPointer<Akonadi2::ResultProvider<typename DomainType::Ptr> > &resultProvider) 88 virtual void read(const Akonadi2::Query &query, const QPair<qint64, qint64> &revisionRange, const QSharedPointer<Akonadi2::ResultProvider<typename DomainType::Ptr> > &resultProvider)
88 { 89 {
89 Akonadi2::Storage storage(Akonadi2::storageLocation(), mResourceInstanceIdentifier); 90 Akonadi2::Storage storage(Akonadi2::storageLocation(), mResourceInstanceIdentifier);
90 storage.setDefaultErrorHandler([](const Akonadi2::Storage::Error &error) { 91 storage.setDefaultErrorHandler([](const Akonadi2::Storage::Error &error) {
diff --git a/common/facade.h b/common/facade.h
index be053f6..d53ec4a 100644
--- a/common/facade.h
+++ b/common/facade.h
@@ -109,7 +109,7 @@ public:
109 GenericFacade(const QByteArray &resourceIdentifier, const DomainTypeAdaptorFactoryInterface::Ptr &adaptorFactory = DomainTypeAdaptorFactoryInterface::Ptr(), const QSharedPointer<EntityStorage<DomainType> > storage = QSharedPointer<EntityStorage<DomainType> >(), const QSharedPointer<Akonadi2::ResourceAccessInterface> resourceAccess = QSharedPointer<Akonadi2::ResourceAccessInterface>()) 109 GenericFacade(const QByteArray &resourceIdentifier, const DomainTypeAdaptorFactoryInterface::Ptr &adaptorFactory = DomainTypeAdaptorFactoryInterface::Ptr(), const QSharedPointer<EntityStorage<DomainType> > storage = QSharedPointer<EntityStorage<DomainType> >(), const QSharedPointer<Akonadi2::ResourceAccessInterface> resourceAccess = QSharedPointer<Akonadi2::ResourceAccessInterface>())
110 : Akonadi2::StoreFacade<DomainType>(), 110 : Akonadi2::StoreFacade<DomainType>(),
111 mResourceAccess(resourceAccess), 111 mResourceAccess(resourceAccess),
112 mStorage(storage ? storage : QSharedPointer<EntityStorage<DomainType> >::create(resourceIdentifier, adaptorFactory)), 112 mStorage(storage ? storage : QSharedPointer<EntityStorage<DomainType> >::create(resourceIdentifier, adaptorFactory, bufferTypeForDomainType())),
113 mDomainTypeAdaptorFactory(adaptorFactory), 113 mDomainTypeAdaptorFactory(adaptorFactory),
114 mResourceInstanceIdentifier(resourceIdentifier) 114 mResourceInstanceIdentifier(resourceIdentifier)
115 { 115 {
diff --git a/common/pipeline.cpp b/common/pipeline.cpp
index 8ef6187..33e5d5c 100644
--- a/common/pipeline.cpp
+++ b/common/pipeline.cpp
@@ -143,7 +143,7 @@ KAsync::Job<void> Pipeline::newEntity(void const *command, size_t size)
143 auto createEntity = Akonadi2::Commands::GetCreateEntity(command); 143 auto createEntity = Akonadi2::Commands::GetCreateEntity(command);
144 144
145 //TODO rename createEntitiy->domainType to bufferType 145 //TODO rename createEntitiy->domainType to bufferType
146 const QString entityType = QString::fromUtf8(reinterpret_cast<char const*>(createEntity->domainType()->Data()), createEntity->domainType()->size()); 146 const QByteArray bufferType = QByteArray(reinterpret_cast<char const*>(createEntity->domainType()->Data()), createEntity->domainType()->size());
147 { 147 {
148 flatbuffers::Verifier verifyer(reinterpret_cast<const uint8_t *>(createEntity->delta()->Data()), createEntity->delta()->size()); 148 flatbuffers::Verifier verifyer(reinterpret_cast<const uint8_t *>(createEntity->delta()->Data()), createEntity->delta()->size());
149 if (!Akonadi2::VerifyEntityBuffer(verifyer)) { 149 if (!Akonadi2::VerifyEntityBuffer(verifyer)) {
@@ -152,6 +152,10 @@ KAsync::Job<void> Pipeline::newEntity(void const *command, size_t size)
152 } 152 }
153 } 153 }
154 auto entity = Akonadi2::GetEntity(createEntity->delta()->Data()); 154 auto entity = Akonadi2::GetEntity(createEntity->delta()->Data());
155 if (!entity->resource()->size() && !entity->local()->size()) {
156 Warning() << "No local and no resource buffer while trying to create entity.";
157 return KAsync::error<void>();
158 }
155 159
156 //Add metadata buffer 160 //Add metadata buffer
157 flatbuffers::FlatBufferBuilder metadataFbb; 161 flatbuffers::FlatBufferBuilder metadataFbb;
@@ -165,14 +169,14 @@ KAsync::Job<void> Pipeline::newEntity(void const *command, size_t size)
165 flatbuffers::FlatBufferBuilder fbb; 169 flatbuffers::FlatBufferBuilder fbb;
166 EntityBuffer::assembleEntityBuffer(fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize(), entity->resource()->Data(), entity->resource()->size(), entity->local()->Data(), entity->local()->size()); 170 EntityBuffer::assembleEntityBuffer(fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize(), entity->resource()->Data(), entity->resource()->size(), entity->local()->Data(), entity->local()->size());
167 171
168 d->transaction.openDatabase().write(key, QByteArray::fromRawData(reinterpret_cast<char const *>(fbb.GetBufferPointer()), fbb.GetSize())); 172 d->transaction.openDatabase(bufferType + ".main").write(key, QByteArray::fromRawData(reinterpret_cast<char const *>(fbb.GetBufferPointer()), fbb.GetSize()));
169 Akonadi2::Storage::setMaxRevision(d->transaction, newRevision); 173 Akonadi2::Storage::setMaxRevision(d->transaction, newRevision);
170 Log() << "Pipeline: wrote entity: " << key << newRevision; 174 Log() << "Pipeline: wrote entity: " << key << newRevision << bufferType;
171 175
172 return KAsync::start<void>([this, key, entityType, newRevision](KAsync::Future<void> &future) { 176 return KAsync::start<void>([this, key, bufferType, newRevision](KAsync::Future<void> &future) {
173 PipelineState state(this, NewPipeline, key, d->newPipeline[entityType], newRevision, [&future]() { 177 PipelineState state(this, NewPipeline, key, d->newPipeline[bufferType], newRevision, [&future]() {
174 future.setFinished(); 178 future.setFinished();
175 }); 179 }, bufferType);
176 d->activePipelines << state; 180 d->activePipelines << state;
177 state.step(); 181 state.step();
178 }); 182 });
@@ -195,10 +199,10 @@ KAsync::Job<void> Pipeline::modifiedEntity(void const *command, size_t size)
195 Q_ASSERT(modifyEntity); 199 Q_ASSERT(modifyEntity);
196 200
197 //TODO rename modifyEntity->domainType to bufferType 201 //TODO rename modifyEntity->domainType to bufferType
198 const QByteArray entityType = QByteArray(reinterpret_cast<char const*>(modifyEntity->domainType()->Data()), modifyEntity->domainType()->size()); 202 const QByteArray bufferType = QByteArray(reinterpret_cast<char const*>(modifyEntity->domainType()->Data()), modifyEntity->domainType()->size());
199 const QByteArray key = QByteArray(reinterpret_cast<char const*>(modifyEntity->entityId()->Data()), modifyEntity->entityId()->size()); 203 const QByteArray key = QByteArray(reinterpret_cast<char const*>(modifyEntity->entityId()->Data()), modifyEntity->entityId()->size());
200 if (entityType.isEmpty() || key.isEmpty()) { 204 if (bufferType.isEmpty() || key.isEmpty()) {
201 Warning() << "entity type or key " << entityType << key; 205 Warning() << "entity type or key " << bufferType << key;
202 return KAsync::error<void>(); 206 return KAsync::error<void>();
203 } 207 }
204 { 208 {
@@ -209,9 +213,9 @@ KAsync::Job<void> Pipeline::modifiedEntity(void const *command, size_t size)
209 } 213 }
210 } 214 }
211 215
212 auto adaptorFactory = d->adaptorFactory.value(entityType); 216 auto adaptorFactory = d->adaptorFactory.value(bufferType);
213 if (!adaptorFactory) { 217 if (!adaptorFactory) {
214 Warning() << "no adaptor factory for type " << entityType; 218 Warning() << "no adaptor factory for type " << bufferType;
215 return KAsync::error<void>(); 219 return KAsync::error<void>();
216 } 220 }
217 221
@@ -220,7 +224,7 @@ KAsync::Job<void> Pipeline::modifiedEntity(void const *command, size_t size)
220 auto diff = adaptorFactory->createAdaptor(*diffEntity); 224 auto diff = adaptorFactory->createAdaptor(*diffEntity);
221 225
222 QSharedPointer<Akonadi2::ApplicationDomain::BufferAdaptor> current; 226 QSharedPointer<Akonadi2::ApplicationDomain::BufferAdaptor> current;
223 storage().createTransaction(Akonadi2::Storage::ReadOnly).openDatabase().scan(key, [&current, adaptorFactory](const QByteArray &key, const QByteArray &data) -> bool { 227 storage().createTransaction(Akonadi2::Storage::ReadOnly).openDatabase(bufferType + ".main").scan(key, [&current, adaptorFactory](const QByteArray &key, const QByteArray &data) -> bool {
224 Akonadi2::EntityBuffer buffer(const_cast<const char *>(data.data()), data.size()); 228 Akonadi2::EntityBuffer buffer(const_cast<const char *>(data.data()), data.size());
225 if (!buffer.isValid()) { 229 if (!buffer.isValid()) {
226 Warning() << "Read invalid buffer from disk"; 230 Warning() << "Read invalid buffer from disk";
@@ -228,6 +232,9 @@ KAsync::Job<void> Pipeline::modifiedEntity(void const *command, size_t size)
228 current = adaptorFactory->createAdaptor(buffer.entity()); 232 current = adaptorFactory->createAdaptor(buffer.entity());
229 } 233 }
230 return false; 234 return false;
235 },
236 [](const Storage::Error &error) {
237 Warning() << "Failed to read value from storage: " << error.message;
231 }); 238 });
232 //TODO error handler 239 //TODO error handler
233 240
@@ -265,13 +272,13 @@ KAsync::Job<void> Pipeline::modifiedEntity(void const *command, size_t size)
265 adaptorFactory->createBuffer(*newObject, fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize()); 272 adaptorFactory->createBuffer(*newObject, fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize());
266 273
267 //TODO don't overwrite the old entry, but instead store a new revision 274 //TODO don't overwrite the old entry, but instead store a new revision
268 d->transaction.openDatabase().write(key, QByteArray::fromRawData(reinterpret_cast<char const *>(fbb.GetBufferPointer()), fbb.GetSize())); 275 d->transaction.openDatabase(bufferType + ".main").write(key, QByteArray::fromRawData(reinterpret_cast<char const *>(fbb.GetBufferPointer()), fbb.GetSize()));
269 Akonadi2::Storage::setMaxRevision(d->transaction, newRevision); 276 Akonadi2::Storage::setMaxRevision(d->transaction, newRevision);
270 277
271 return KAsync::start<void>([this, key, entityType, newRevision](KAsync::Future<void> &future) { 278 return KAsync::start<void>([this, key, bufferType, newRevision](KAsync::Future<void> &future) {
272 PipelineState state(this, ModifiedPipeline, key, d->modifiedPipeline[entityType], newRevision, [&future]() { 279 PipelineState state(this, ModifiedPipeline, key, d->modifiedPipeline[bufferType], newRevision, [&future]() {
273 future.setFinished(); 280 future.setFinished();
274 }); 281 }, bufferType);
275 d->activePipelines << state; 282 d->activePipelines << state;
276 state.step(); 283 state.step();
277 }); 284 });
@@ -292,18 +299,18 @@ KAsync::Job<void> Pipeline::deletedEntity(void const *command, size_t size)
292 } 299 }
293 auto deleteEntity = Akonadi2::Commands::GetDeleteEntity(command); 300 auto deleteEntity = Akonadi2::Commands::GetDeleteEntity(command);
294 301
295 const QByteArray entityType = QByteArray(reinterpret_cast<char const*>(deleteEntity->domainType()->Data()), deleteEntity->domainType()->size()); 302 const QByteArray bufferType = QByteArray(reinterpret_cast<char const*>(deleteEntity->domainType()->Data()), deleteEntity->domainType()->size());
296 const QByteArray key = QByteArray(reinterpret_cast<char const*>(deleteEntity->entityId()->Data()), deleteEntity->entityId()->size()); 303 const QByteArray key = QByteArray(reinterpret_cast<char const*>(deleteEntity->entityId()->Data()), deleteEntity->entityId()->size());
297 304
298 //TODO instead of deleting the entry, a new revision should be created that marks the entity as deleted 305 //TODO instead of deleting the entry, a new revision should be created that marks the entity as deleted
299 d->transaction.openDatabase().remove(key); 306 d->transaction.openDatabase(bufferType + ".main").remove(key);
300 Akonadi2::Storage::setMaxRevision(d->transaction, newRevision); 307 Akonadi2::Storage::setMaxRevision(d->transaction, newRevision);
301 Log() << "Pipeline: deleted entity: "<< newRevision; 308 Log() << "Pipeline: deleted entity: "<< newRevision;
302 309
303 return KAsync::start<void>([this, key, entityType, newRevision](KAsync::Future<void> &future) { 310 return KAsync::start<void>([this, key, bufferType, newRevision](KAsync::Future<void> &future) {
304 PipelineState state(this, DeletedPipeline, key, d->deletedPipeline[entityType], newRevision, [&future](){ 311 PipelineState state(this, DeletedPipeline, key, d->deletedPipeline[bufferType], newRevision, [&future](){
305 future.setFinished(); 312 future.setFinished();
306 }); 313 }, bufferType);
307 d->activePipelines << state; 314 d->activePipelines << state;
308 state.step(); 315 state.step();
309 }); 316 });
@@ -354,14 +361,15 @@ void Pipeline::pipelineCompleted(PipelineState state)
354class PipelineState::Private : public QSharedData 361class PipelineState::Private : public QSharedData
355{ 362{
356public: 363public:
357 Private(Pipeline *p, Pipeline::Type t, const QByteArray &k, QVector<Preprocessor *> filters, const std::function<void()> &c, qint64 r) 364 Private(Pipeline *p, Pipeline::Type t, const QByteArray &k, QVector<Preprocessor *> filters, const std::function<void()> &c, qint64 r, const QByteArray &b)
358 : pipeline(p), 365 : pipeline(p),
359 type(t), 366 type(t),
360 key(k), 367 key(k),
361 filterIt(filters), 368 filterIt(filters),
362 idle(true), 369 idle(true),
363 callback(c), 370 callback(c),
364 revision(r) 371 revision(r),
372 bufferType(b)
365 {} 373 {}
366 374
367 Private() 375 Private()
@@ -378,6 +386,7 @@ public:
378 bool idle; 386 bool idle;
379 std::function<void()> callback; 387 std::function<void()> callback;
380 qint64 revision; 388 qint64 revision;
389 QByteArray bufferType;
381}; 390};
382 391
383PipelineState::PipelineState() 392PipelineState::PipelineState()
@@ -386,8 +395,8 @@ PipelineState::PipelineState()
386 395
387} 396}
388 397
389PipelineState::PipelineState(Pipeline *pipeline, Pipeline::Type type, const QByteArray &key, const QVector<Preprocessor *> &filters, qint64 revision, const std::function<void()> &callback) 398PipelineState::PipelineState(Pipeline *pipeline, Pipeline::Type type, const QByteArray &key, const QVector<Preprocessor *> &filters, qint64 revision, const std::function<void()> &callback, const QByteArray &bufferType)
390 : d(new Private(pipeline, type, key, filters, callback, revision)) 399 : d(new Private(pipeline, type, key, filters, callback, revision, bufferType))
391{ 400{
392} 401}
393 402
@@ -431,6 +440,11 @@ qint64 PipelineState::revision() const
431 return d->revision; 440 return d->revision;
432} 441}
433 442
443QByteArray PipelineState::bufferType() const
444{
445 return d->bufferType;
446}
447
434void PipelineState::step() 448void PipelineState::step()
435{ 449{
436 if (!d->pipeline) { 450 if (!d->pipeline) {
diff --git a/common/pipeline.h b/common/pipeline.h
index a3b3735..573af73 100644
--- a/common/pipeline.h
+++ b/common/pipeline.h
@@ -85,7 +85,7 @@ class AKONADI2COMMON_EXPORT PipelineState
85{ 85{
86public: 86public:
87 PipelineState(); 87 PipelineState();
88 PipelineState(Pipeline *pipeline, Pipeline::Type type, const QByteArray &key, const QVector<Preprocessor *> &filters, qint64 revision, const std::function<void()> &callback); 88 PipelineState(Pipeline *pipeline, Pipeline::Type type, const QByteArray &key, const QVector<Preprocessor *> &filters, qint64 revision, const std::function<void()> &callback, const QByteArray &bufferType);
89 PipelineState(const PipelineState &other); 89 PipelineState(const PipelineState &other);
90 ~PipelineState(); 90 ~PipelineState();
91 91
@@ -96,7 +96,7 @@ public:
96 QByteArray key() const; 96 QByteArray key() const;
97 Pipeline::Type type() const; 97 Pipeline::Type type() const;
98 qint64 revision() const; 98 qint64 revision() const;
99 //TODO expose command 99 QByteArray bufferType() const;
100 100
101 void step(); 101 void step();
102 void processingCompleted(Preprocessor *filter); 102 void processingCompleted(Preprocessor *filter);
@@ -114,7 +114,6 @@ public:
114 Preprocessor(); 114 Preprocessor();
115 virtual ~Preprocessor(); 115 virtual ~Preprocessor();
116 116
117 //TODO pass actual command as well, for changerecording
118 virtual void process(const PipelineState &state, Akonadi2::Storage::Transaction &transaction); 117 virtual void process(const PipelineState &state, Akonadi2::Storage::Transaction &transaction);
119 //TODO to record progress 118 //TODO to record progress
120 virtual QString id() const; 119 virtual QString id() const;
@@ -142,7 +141,7 @@ public:
142 141
143 void process(const PipelineState &state, Akonadi2::Storage::Transaction &transaction) Q_DECL_OVERRIDE 142 void process(const PipelineState &state, Akonadi2::Storage::Transaction &transaction) Q_DECL_OVERRIDE
144 { 143 {
145 transaction.openDatabase().scan(state.key(), [this, &state, &transaction](const QByteArray &key, const QByteArray &value) -> bool { 144 transaction.openDatabase(state.bufferType() + ".main").scan(state.key(), [this, &state, &transaction](const QByteArray &key, const QByteArray &value) -> bool {
146 auto entity = Akonadi2::GetEntity(value); 145 auto entity = Akonadi2::GetEntity(value);
147 mFunction(state, *entity, transaction); 146 mFunction(state, *entity, transaction);
148 processingCompleted(state); 147 processingCompleted(state);
diff --git a/common/propertymapper.cpp b/common/propertymapper.cpp
index 89495ae..7ff072a 100644
--- a/common/propertymapper.cpp
+++ b/common/propertymapper.cpp
@@ -38,3 +38,9 @@ QVariant propertyToVariant<QString>(const flatbuffers::String *property)
38 return QVariant(); 38 return QVariant();
39} 39}
40 40
41template <>
42QVariant propertyToVariant<bool>(uint8_t property)
43{
44 return static_cast<bool>(property);
45}
46
diff --git a/common/propertymapper.h b/common/propertymapper.h
index 0c6c16f..72468e2 100644
--- a/common/propertymapper.h
+++ b/common/propertymapper.h
@@ -35,6 +35,8 @@ flatbuffers::uoffset_t variantToProperty(const QVariant &, flatbuffers::FlatBuff
35 */ 35 */
36template <typename T> 36template <typename T>
37QVariant propertyToVariant(const flatbuffers::String *); 37QVariant propertyToVariant(const flatbuffers::String *);
38template <typename T>
39QVariant propertyToVariant(uint8_t);
38 40
39 41
40/** 42/**
diff --git a/common/storage.h b/common/storage.h
index 191f535..d186b2e 100644
--- a/common/storage.h
+++ b/common/storage.h
@@ -161,8 +161,6 @@ public:
161 qint64 diskUsage() const; 161 qint64 diskUsage() const;
162 void removeFromDisk() const; 162 void removeFromDisk() const;
163 163
164 qint64 maxRevision();
165 void setMaxRevision(qint64 revision);
166 static qint64 maxRevision(const Akonadi2::Storage::Transaction &); 164 static qint64 maxRevision(const Akonadi2::Storage::Transaction &);
167 static void setMaxRevision(Akonadi2::Storage::Transaction &, qint64 revision); 165 static void setMaxRevision(Akonadi2::Storage::Transaction &, qint64 revision);
168 166
diff --git a/common/storage_common.cpp b/common/storage_common.cpp
index a506cf8..f22150a 100644
--- a/common/storage_common.cpp
+++ b/common/storage_common.cpp
@@ -54,23 +54,11 @@ std::function<void(const Storage::Error &error)> Storage::defaultErrorHandler()
54 return basicErrorHandler(); 54 return basicErrorHandler();
55} 55}
56 56
57void Storage::setMaxRevision(qint64 revision)
58{
59 auto transaction = createTransaction(Akonadi2::Storage::ReadWrite);
60 setMaxRevision(transaction, revision);
61}
62
63void Storage::setMaxRevision(Akonadi2::Storage::Transaction &transaction, qint64 revision) 57void Storage::setMaxRevision(Akonadi2::Storage::Transaction &transaction, qint64 revision)
64{ 58{
65 transaction.openDatabase().write("__internal_maxRevision", QByteArray::number(revision)); 59 transaction.openDatabase().write("__internal_maxRevision", QByteArray::number(revision));
66} 60}
67 61
68qint64 Storage::maxRevision()
69{
70 auto transaction = createTransaction(Akonadi2::Storage::ReadOnly);
71 return maxRevision(transaction);
72}
73
74qint64 Storage::maxRevision(const Akonadi2::Storage::Transaction &transaction) 62qint64 Storage::maxRevision(const Akonadi2::Storage::Transaction &transaction)
75{ 63{
76 qint64 r = 0; 64 qint64 r = 0;
diff --git a/examples/dummyresource/domainadaptor.cpp b/examples/dummyresource/domainadaptor.cpp
index 4cc592e..d08a783 100644
--- a/examples/dummyresource/domainadaptor.cpp
+++ b/examples/dummyresource/domainadaptor.cpp
@@ -45,3 +45,9 @@ DummyEventAdaptorFactory::DummyEventAdaptorFactory()
45 }); 45 });
46} 46}
47 47
48DummyMailAdaptorFactory::DummyMailAdaptorFactory()
49 : DomainTypeAdaptorFactory()
50{
51
52}
53
diff --git a/examples/dummyresource/domainadaptor.h b/examples/dummyresource/domainadaptor.h
index 8b6d96b..add3e8e 100644
--- a/examples/dummyresource/domainadaptor.h
+++ b/examples/dummyresource/domainadaptor.h
@@ -20,6 +20,7 @@
20 20
21#include "common/domainadaptor.h" 21#include "common/domainadaptor.h"
22#include "event_generated.h" 22#include "event_generated.h"
23#include "mail_generated.h"
23#include "dummycalendar_generated.h" 24#include "dummycalendar_generated.h"
24#include "entity_generated.h" 25#include "entity_generated.h"
25 26
@@ -29,3 +30,11 @@ public:
29 DummyEventAdaptorFactory(); 30 DummyEventAdaptorFactory();
30 virtual ~DummyEventAdaptorFactory() {}; 31 virtual ~DummyEventAdaptorFactory() {};
31}; 32};
33
34//TODO replace the resource specific event class by a mail class or a dummy class if no resource type is required.
35class DummyMailAdaptorFactory : public DomainTypeAdaptorFactory<Akonadi2::ApplicationDomain::Mail, DummyCalendar::DummyEvent, DummyCalendar::DummyEventBuilder>
36{
37public:
38 DummyMailAdaptorFactory();
39 virtual ~DummyMailAdaptorFactory() {};
40};
diff --git a/examples/dummyresource/dummystore.cpp b/examples/dummyresource/dummystore.cpp
index 5a3f74b..41b48ed 100644
--- a/examples/dummyresource/dummystore.cpp
+++ b/examples/dummyresource/dummystore.cpp
@@ -21,6 +21,7 @@
21#include <QString> 21#include <QString>
22 22
23#include "dummycalendar_generated.h" 23#include "dummycalendar_generated.h"
24#include "mail_generated.h"
24 25
25static std::string createEvent(int i) 26static std::string createEvent(int i)
26{ 27{
@@ -43,6 +44,20 @@ static std::string createEvent(int i)
43 return std::string(reinterpret_cast<const char *>(fbb.GetBufferPointer()), fbb.GetSize()); 44 return std::string(reinterpret_cast<const char *>(fbb.GetBufferPointer()), fbb.GetSize());
44} 45}
45 46
47static std::string createMail(int i)
48{
49 static flatbuffers::FlatBufferBuilder fbb;
50 fbb.Clear();
51 {
52 auto subject = fbb.CreateString("summary" + std::to_string(i));
53 Akonadi2::ApplicationDomain::Buffer::MailBuilder mailBuilder(fbb);
54 mailBuilder.add_subject(subject);
55 Akonadi2::ApplicationDomain::Buffer::FinishMailBuffer(fbb, mailBuilder.Finish());
56 }
57
58 return std::string(reinterpret_cast<const char *>(fbb.GetBufferPointer()), fbb.GetSize());
59}
60
46QMap<QString, QString> populate() 61QMap<QString, QString> populate()
47{ 62{
48 QMap<QString, QString> content; 63 QMap<QString, QString> content;
@@ -53,10 +68,24 @@ QMap<QString, QString> populate()
53 return content; 68 return content;
54} 69}
55 70
56static QMap<QString, QString> s_dataSource = populate(); 71QMap<QString, QString> populateMails()
72{
73 QMap<QString, QString> content;
74 for (int i = 0; i < 2; i++) {
75 content.insert(QString("key%1").arg(i), QString::fromStdString(createMail(i)));
76 }
77 return content;
78}
57 79
80static QMap<QString, QString> s_dataSource = populate();
81static QMap<QString, QString> s_mailSource = populateMails();
58 82
59QMap<QString, QString> DummyStore::data() const 83QMap<QString, QString> DummyStore::events() const
60{ 84{
61 return s_dataSource; 85 return s_dataSource;
62} 86}
87
88QMap<QString, QString> DummyStore::mails() const
89{
90 return s_mailSource;
91}
diff --git a/examples/dummyresource/dummystore.h b/examples/dummyresource/dummystore.h
index 6a404ae..ba1c0ae 100644
--- a/examples/dummyresource/dummystore.h
+++ b/examples/dummyresource/dummystore.h
@@ -29,6 +29,6 @@ public:
29 return instance; 29 return instance;
30 } 30 }
31 31
32 QMap<QString, QString> data() const; 32 QMap<QString, QString> events() const;
33 33 QMap<QString, QString> mails() const;
34}; 34};
diff --git a/examples/dummyresource/facade.cpp b/examples/dummyresource/facade.cpp
index d20d12d..5a9d722 100644
--- a/examples/dummyresource/facade.cpp
+++ b/examples/dummyresource/facade.cpp
@@ -30,3 +30,34 @@ DummyResourceFacade::~DummyResourceFacade()
30{ 30{
31} 31}
32 32
33DummyResourceMailFacade::DummyResourceMailFacade(const QByteArray &instanceIdentifier)
34 : Akonadi2::GenericFacade<Akonadi2::ApplicationDomain::Mail>(instanceIdentifier, QSharedPointer<DummyMailAdaptorFactory>::create())
35{
36}
37
38DummyResourceMailFacade::~DummyResourceMailFacade()
39{
40}
41
42static void addFolder(const QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Folder::Ptr> > &resultProvider, QByteArray uid, QString name, QString icon)
43{
44 auto folder = Akonadi2::ApplicationDomain::Folder::Ptr::create();
45 folder->setProperty("name", name);
46 folder->setProperty("uid", uid);
47 resultProvider->add(folder);
48}
49
50KAsync::Job<void> load(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Folder::Ptr> > &resultProvider)
51{
52 //Dummy implementation for a fixed set of folders
53 addFolder(resultProvider, "inbox", "INBOX", "mail-folder-inbox");
54 addFolder(resultProvider, "sent", "Sent", "mail-folder-sent");
55 addFolder(resultProvider, "trash", "Trash", "user-trash");
56 addFolder(resultProvider, "drafts", "Drafts", "document-edit");
57 addFolder(resultProvider, "1", "dragons", "folder");
58 addFolder(resultProvider, "1", "super mega long tailed dragons", "folder");
59 resultProvider->initialResultSetComplete();
60 resultProvider->complete();
61 return KAsync::null<void>();
62}
63
diff --git a/examples/dummyresource/facade.h b/examples/dummyresource/facade.h
index dde0dc2..948116b 100644
--- a/examples/dummyresource/facade.h
+++ b/examples/dummyresource/facade.h
@@ -28,3 +28,21 @@ public:
28 DummyResourceFacade(const QByteArray &instanceIdentifier); 28 DummyResourceFacade(const QByteArray &instanceIdentifier);
29 virtual ~DummyResourceFacade(); 29 virtual ~DummyResourceFacade();
30}; 30};
31
32class DummyResourceMailFacade : public Akonadi2::GenericFacade<Akonadi2::ApplicationDomain::Mail>
33{
34public:
35 DummyResourceMailFacade(const QByteArray &instanceIdentifier);
36 virtual ~DummyResourceMailFacade();
37};
38
39class DummyResourceFolderFacade : public Akonadi2::StoreFacade<Akonadi2::ApplicationDomain::Folder>
40{
41public:
42 DummyResourceFolderFacade(const QByteArray &instanceIdentifier);
43 virtual ~DummyResourceFolderFacade();
44 virtual KAsync::Job<void> create(const Akonadi2::ApplicationDomain::Folder &domainObject) { return KAsync::null<void>(); };
45 virtual KAsync::Job<void> modify(const Akonadi2::ApplicationDomain::Folder &domainObject) { return KAsync::null<void>(); };
46 virtual KAsync::Job<void> remove(const Akonadi2::ApplicationDomain::Folder &domainObject) { return KAsync::null<void>(); };
47 virtual KAsync::Job<void> load(const Akonadi2::Query &query, const QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Folder::Ptr> > &resultProvider);
48};
diff --git a/examples/dummyresource/resourcefactory.cpp b/examples/dummyresource/resourcefactory.cpp
index 0e18282..147cb22 100644
--- a/examples/dummyresource/resourcefactory.cpp
+++ b/examples/dummyresource/resourcefactory.cpp
@@ -23,6 +23,7 @@
23#include "entitybuffer.h" 23#include "entitybuffer.h"
24#include "pipeline.h" 24#include "pipeline.h"
25#include "dummycalendar_generated.h" 25#include "dummycalendar_generated.h"
26#include "mail_generated.h"
26#include "queuedcommand_generated.h" 27#include "queuedcommand_generated.h"
27#include "createentity_generated.h" 28#include "createentity_generated.h"
28#include "domainadaptor.h" 29#include "domainadaptor.h"
@@ -30,21 +31,23 @@
30#include "index.h" 31#include "index.h"
31#include "log.h" 32#include "log.h"
32#include "domain/event.h" 33#include "domain/event.h"
34#include "domain/mail.h"
33#include "dummystore.h" 35#include "dummystore.h"
34#include "definitions.h" 36#include "definitions.h"
35#include "facadefactory.h" 37#include "facadefactory.h"
36 38
37//This is the resources entity type, and not the domain type 39//This is the resources entity type, and not the domain type
38#define ENTITY_TYPE_EVENT "event" 40#define ENTITY_TYPE_EVENT "event"
41#define ENTITY_TYPE_MAIL "mail"
39 42
40DummyResource::DummyResource(const QByteArray &instanceIdentifier, const QSharedPointer<Akonadi2::Pipeline> &pipeline) 43DummyResource::DummyResource(const QByteArray &instanceIdentifier, const QSharedPointer<Akonadi2::Pipeline> &pipeline)
41 : Akonadi2::GenericResource(instanceIdentifier, pipeline) 44 : Akonadi2::GenericResource(instanceIdentifier, pipeline)
42{ 45{
43 auto eventFactory = QSharedPointer<DummyEventAdaptorFactory>::create(); 46 auto eventFactory = QSharedPointer<DummyEventAdaptorFactory>::create();
44 const auto resourceIdentifier = mResourceInstanceIdentifier; 47 const auto resourceIdentifier = mResourceInstanceIdentifier;
48
45 auto eventIndexer = new Akonadi2::SimpleProcessor("eventIndexer", [eventFactory, resourceIdentifier](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity, Akonadi2::Storage::Transaction &transaction) { 49 auto eventIndexer = new Akonadi2::SimpleProcessor("eventIndexer", [eventFactory, resourceIdentifier](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity, Akonadi2::Storage::Transaction &transaction) {
46 auto adaptor = eventFactory->createAdaptor(entity); 50 auto adaptor = eventFactory->createAdaptor(entity);
47 //FIXME set revision?
48 Akonadi2::ApplicationDomain::Event event(resourceIdentifier, state.key(), -1, adaptor); 51 Akonadi2::ApplicationDomain::Event event(resourceIdentifier, state.key(), -1, adaptor);
49 Akonadi2::ApplicationDomain::TypeImplementation<Akonadi2::ApplicationDomain::Event>::index(event, transaction); 52 Akonadi2::ApplicationDomain::TypeImplementation<Akonadi2::ApplicationDomain::Event>::index(event, transaction);
50 53
@@ -58,15 +61,79 @@ DummyResource::DummyResource(const QByteArray &instanceIdentifier, const QShared
58 mPipeline->setPreprocessors(ENTITY_TYPE_EVENT, Akonadi2::Pipeline::NewPipeline, QVector<Akonadi2::Preprocessor*>() << eventIndexer); 61 mPipeline->setPreprocessors(ENTITY_TYPE_EVENT, Akonadi2::Pipeline::NewPipeline, QVector<Akonadi2::Preprocessor*>() << eventIndexer);
59 mPipeline->setAdaptorFactory(ENTITY_TYPE_EVENT, eventFactory); 62 mPipeline->setAdaptorFactory(ENTITY_TYPE_EVENT, eventFactory);
60 //TODO cleanup indexes during removal 63 //TODO cleanup indexes during removal
64
65 {
66 auto mailFactory = QSharedPointer<DummyMailAdaptorFactory>::create();
67 auto mailIndexer = new Akonadi2::SimpleProcessor("mailIndexer", [mailFactory, resourceIdentifier](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity, Akonadi2::Storage::Transaction &transaction) {
68 auto adaptor = mailFactory->createAdaptor(entity);
69 Akonadi2::ApplicationDomain::Mail mail(resourceIdentifier, state.key(), -1, adaptor);
70 Akonadi2::ApplicationDomain::TypeImplementation<Akonadi2::ApplicationDomain::Mail>::index(mail, transaction);
71
72 Index ridIndex("mail.index.rid", transaction);
73 const auto rid = mail.getProperty("remoteId");
74 if (rid.isValid()) {
75 ridIndex.add(rid.toByteArray(), mail.identifier());
76 }
77 });
78
79 mPipeline->setPreprocessors(ENTITY_TYPE_MAIL, Akonadi2::Pipeline::NewPipeline, QVector<Akonadi2::Preprocessor*>() << mailIndexer);
80 mPipeline->setAdaptorFactory(ENTITY_TYPE_MAIL, mailFactory);
81 }
82}
83
84void DummyResource::createEvent(const QByteArray &ridBuffer, const QByteArray &data, flatbuffers::FlatBufferBuilder &entityFbb)
85{
86 auto eventBuffer = DummyCalendar::GetDummyEvent(data.data());
87
88 //Map the source format to the buffer format (which happens to be an exact copy here)
89 auto summary = m_fbb.CreateString(eventBuffer->summary()->c_str());
90 auto rid = m_fbb.CreateString(std::string(ridBuffer.constData(), ridBuffer.size()));
91 auto description = m_fbb.CreateString(std::string(ridBuffer.constData(), ridBuffer.size()));
92 static uint8_t rawData[100];
93 auto attachment = Akonadi2::EntityBuffer::appendAsVector(m_fbb, rawData, 100);
94
95 auto builder = DummyCalendar::DummyEventBuilder(m_fbb);
96 builder.add_summary(summary);
97 builder.add_remoteId(rid);
98 builder.add_description(description);
99 builder.add_attachment(attachment);
100 auto buffer = builder.Finish();
101 DummyCalendar::FinishDummyEventBuffer(m_fbb, buffer);
102 Akonadi2::EntityBuffer::assembleEntityBuffer(entityFbb, 0, 0, m_fbb.GetBufferPointer(), m_fbb.GetSize(), 0, 0);
103}
104
105void DummyResource::createMail(const QByteArray &ridBuffer, const QByteArray &data, flatbuffers::FlatBufferBuilder &entityFbb)
106{
107 auto mailBuffer = Akonadi2::ApplicationDomain::Buffer::GetMail(data.data());
108
109 //Map the source format to the buffer format (which happens to be an exact copy here)
110 auto subject = m_fbb.CreateString(mailBuffer->subject()->c_str());
111 auto rid = m_fbb.CreateString(std::string(ridBuffer.constData(), ridBuffer.size()));
112 auto sender = m_fbb.CreateString(std::string("sender@example.org"));
113 auto senderName = m_fbb.CreateString(std::string("Sender Name"));
114 auto date = m_fbb.CreateString(std::string("2004"));
115 auto folder = m_fbb.CreateString(std::string("inbox"));
116
117 auto builder = Akonadi2::ApplicationDomain::Buffer::MailBuilder(m_fbb);
118 builder.add_subject(subject);
119 builder.add_sender(sender);
120 builder.add_senderName(senderName);
121 builder.add_unread(true);
122 builder.add_important(false);
123 builder.add_date(date);
124 builder.add_folder(folder);
125 auto buffer = builder.Finish();
126 Akonadi2::ApplicationDomain::Buffer::FinishMailBuffer(m_fbb, buffer);
127 Akonadi2::EntityBuffer::assembleEntityBuffer(entityFbb, 0, 0, 0, 0, m_fbb.GetBufferPointer(), m_fbb.GetSize());
61} 128}
62 129
63KAsync::Job<void> DummyResource::synchronizeWithSource() 130KAsync::Job<void> DummyResource::synchronizeWithSource()
64{ 131{
65 return KAsync::start<void>([this](KAsync::Future<void> &f) { 132 return KAsync::start<void>([this](KAsync::Future<void> &f) {
66 auto transaction = Akonadi2::Storage(Akonadi2::storageLocation(), mResourceInstanceIdentifier + ".index.uid", Akonadi2::Storage::ReadOnly).createTransaction(Akonadi2::Storage::ReadOnly); 133 auto transaction = Akonadi2::Storage(Akonadi2::storageLocation(), mResourceInstanceIdentifier, Akonadi2::Storage::ReadOnly).createTransaction(Akonadi2::Storage::ReadOnly);
67 Index uidIndex("index.uid", transaction); 134 Index uidIndex("index.uid", transaction);
68 135
69 const auto data = DummyStore::instance().data(); 136 const auto data = DummyStore::instance().events();
70 for (auto it = data.constBegin(); it != data.constEnd(); it++) { 137 for (auto it = data.constBegin(); it != data.constEnd(); it++) {
71 bool isNew = true; 138 bool isNew = true;
72 uidIndex.lookup(it.key().toLatin1(), [&](const QByteArray &value) { 139 uidIndex.lookup(it.key().toLatin1(), [&](const QByteArray &value) {
@@ -80,25 +147,8 @@ KAsync::Job<void> DummyResource::synchronizeWithSource()
80 if (isNew) { 147 if (isNew) {
81 m_fbb.Clear(); 148 m_fbb.Clear();
82 149
83 const QByteArray data = it.value().toUtf8();
84 auto eventBuffer = DummyCalendar::GetDummyEvent(data.data());
85
86 //Map the source format to the buffer format (which happens to be an exact copy here)
87 auto summary = m_fbb.CreateString(eventBuffer->summary()->c_str());
88 auto rid = m_fbb.CreateString(it.key().toStdString().c_str());
89 auto description = m_fbb.CreateString(it.key().toStdString().c_str());
90 static uint8_t rawData[100];
91 auto attachment = Akonadi2::EntityBuffer::appendAsVector(m_fbb, rawData, 100);
92
93 auto builder = DummyCalendar::DummyEventBuilder(m_fbb);
94 builder.add_summary(summary);
95 builder.add_remoteId(rid);
96 builder.add_description(description);
97 builder.add_attachment(attachment);
98 auto buffer = builder.Finish();
99 DummyCalendar::FinishDummyEventBuffer(m_fbb, buffer);
100 flatbuffers::FlatBufferBuilder entityFbb; 150 flatbuffers::FlatBufferBuilder entityFbb;
101 Akonadi2::EntityBuffer::assembleEntityBuffer(entityFbb, 0, 0, m_fbb.GetBufferPointer(), m_fbb.GetSize(), 0, 0); 151 createEvent(it.key().toUtf8(), it.value().toUtf8(), entityFbb);
102 152
103 flatbuffers::FlatBufferBuilder fbb; 153 flatbuffers::FlatBufferBuilder fbb;
104 //This is the resource type and not the domain type 154 //This is the resource type and not the domain type
@@ -113,6 +163,43 @@ KAsync::Job<void> DummyResource::synchronizeWithSource()
113 } 163 }
114 } 164 }
115 //TODO find items to remove 165 //TODO find items to remove
166
167 const auto mails = DummyStore::instance().mails();
168 for (auto it = mails.constBegin(); it != mails.constEnd(); it++) {
169 bool isNew = true;
170 uidIndex.lookup(it.key().toLatin1(), [&](const QByteArray &value) {
171 isNew = false;
172 },
173 [](const Index::Error &error) {
174 if (error.code != Index::IndexNotAvailable) {
175 Warning() << "Error in uid index: " << error.message;
176 }
177 });
178 if (isNew) {
179 m_fbb.Clear();
180
181 flatbuffers::FlatBufferBuilder entityFbb;
182 createMail(it.key().toUtf8(), it.value().toUtf8(), entityFbb);
183
184 flatbuffers::Verifier verifyer(reinterpret_cast<const uint8_t *>(entityFbb.GetBufferPointer()), entityFbb.GetSize());
185 if (!Akonadi2::ApplicationDomain::Buffer::VerifyMailBuffer(verifyer)) {
186 Warning() << "invalid buffer, not a mail buffer";
187 }
188
189 flatbuffers::FlatBufferBuilder fbb;
190 //This is the resource type and not the domain type
191 auto type = fbb.CreateString(ENTITY_TYPE_MAIL);
192 auto delta = Akonadi2::EntityBuffer::appendAsVector(fbb, entityFbb.GetBufferPointer(), entityFbb.GetSize());
193 auto location = Akonadi2::Commands::CreateCreateEntity(fbb, type, delta);
194 Akonadi2::Commands::FinishCreateEntityBuffer(fbb, location);
195
196 enqueueCommand(mSynchronizerQueue, Akonadi2::Commands::CreateEntityCommand, QByteArray::fromRawData(reinterpret_cast<char const *>(fbb.GetBufferPointer()), fbb.GetSize()));
197 } else { //modification
198 //TODO diff and create modification if necessary
199 }
200 }
201 //TODO find items to remove
202
116 f.setFinished(); 203 f.setFinished();
117 }); 204 });
118} 205}
@@ -132,6 +219,7 @@ Akonadi2::Resource *DummyResourceFactory::createResource(const QByteArray &insta
132void DummyResourceFactory::registerFacades(Akonadi2::FacadeFactory &factory) 219void DummyResourceFactory::registerFacades(Akonadi2::FacadeFactory &factory)
133{ 220{
134 factory.registerFacade<Akonadi2::ApplicationDomain::Event, DummyResourceFacade>(PLUGIN_NAME); 221 factory.registerFacade<Akonadi2::ApplicationDomain::Event, DummyResourceFacade>(PLUGIN_NAME);
222 factory.registerFacade<Akonadi2::ApplicationDomain::Mail, DummyResourceMailFacade>(PLUGIN_NAME);
135} 223}
136 224
137#include "resourcefactory.moc" 225#include "resourcefactory.moc"
diff --git a/examples/dummyresource/resourcefactory.h b/examples/dummyresource/resourcefactory.h
index 4baafa7..cf0f624 100644
--- a/examples/dummyresource/resourcefactory.h
+++ b/examples/dummyresource/resourcefactory.h
@@ -34,6 +34,9 @@ class DummyResource : public Akonadi2::GenericResource
34public: 34public:
35 DummyResource(const QByteArray &instanceIdentifier, const QSharedPointer<Akonadi2::Pipeline> &pipeline = QSharedPointer<Akonadi2::Pipeline>()); 35 DummyResource(const QByteArray &instanceIdentifier, const QSharedPointer<Akonadi2::Pipeline> &pipeline = QSharedPointer<Akonadi2::Pipeline>());
36 KAsync::Job<void> synchronizeWithSource() Q_DECL_OVERRIDE; 36 KAsync::Job<void> synchronizeWithSource() Q_DECL_OVERRIDE;
37private:
38 void createEvent(const QByteArray &rid, const QByteArray &data, flatbuffers::FlatBufferBuilder &entityFbb);
39 void createMail(const QByteArray &rid, const QByteArray &data, flatbuffers::FlatBufferBuilder &entityFbb);
37}; 40};
38 41
39class DummyResourceFactory : public Akonadi2::ResourceFactory 42class DummyResourceFactory : public Akonadi2::ResourceFactory
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 1c75e96..251e780 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -31,6 +31,7 @@ manual_tests (
31 storagebenchmark 31 storagebenchmark
32 dummyresourcebenchmark 32 dummyresourcebenchmark
33 genericresourcebenchmark 33 genericresourcebenchmark
34 genericfacadebenchmark
34) 35)
35 36
36auto_tests ( 37auto_tests (
diff --git a/tests/dummyresourcetest.cpp b/tests/dummyresourcetest.cpp
index e3b3f07..a28e071 100644
--- a/tests/dummyresourcetest.cpp
+++ b/tests/dummyresourcetest.cpp
@@ -164,6 +164,21 @@ private Q_SLOTS:
164 qDebug() << value->getProperty("summary").toString(); 164 qDebug() << value->getProperty("summary").toString();
165 } 165 }
166 166
167 void testSyncAndFacadeMail()
168 {
169 Akonadi2::Query query;
170 query.resources << "org.kde.dummy.instance1";
171 query.syncOnDemand = true;
172 query.processAll = true;
173
174 async::SyncListResult<Akonadi2::ApplicationDomain::Mail::Ptr> result(Akonadi2::Store::load<Akonadi2::ApplicationDomain::Mail>(query));
175 result.exec();
176 QVERIFY(!result.isEmpty());
177 auto value = result.first();
178 QVERIFY(!value->getProperty("subject").toString().isEmpty());
179 qDebug() << value->getProperty("subject").toString();
180 }
181
167 void testWriteModifyDelete() 182 void testWriteModifyDelete()
168 { 183 {
169 Akonadi2::ApplicationDomain::Event event; 184 Akonadi2::ApplicationDomain::Event event;
diff --git a/tests/genericfacadebenchmark.cpp b/tests/genericfacadebenchmark.cpp
new file mode 100644
index 0000000..7cd6c75
--- /dev/null
+++ b/tests/genericfacadebenchmark.cpp
@@ -0,0 +1,111 @@
1#include <QtTest>
2
3#include <QString>
4
5#include <common/facade.h>
6#include <common/domainadaptor.h>
7#include <common/resultprovider.h>
8#include <common/synclistresult.h>
9
10#include "event_generated.h"
11
12class TestEventAdaptorFactory : public DomainTypeAdaptorFactory<Akonadi2::ApplicationDomain::Event, Akonadi2::ApplicationDomain::Buffer::Event, Akonadi2::ApplicationDomain::Buffer::EventBuilder>
13{
14public:
15 TestEventAdaptorFactory()
16 : DomainTypeAdaptorFactory()
17 {
18 }
19
20 virtual ~TestEventAdaptorFactory() {};
21};
22
23class TestResourceAccess : public Akonadi2::ResourceAccessInterface
24{
25 Q_OBJECT
26public:
27 virtual ~TestResourceAccess() {};
28 KAsync::Job<void> sendCommand(int commandId) Q_DECL_OVERRIDE { return KAsync::null<void>(); }
29 KAsync::Job<void> sendCommand(int commandId, flatbuffers::FlatBufferBuilder &fbb) Q_DECL_OVERRIDE { return KAsync::null<void>(); }
30 KAsync::Job<void> synchronizeResource(bool remoteSync, bool localSync) Q_DECL_OVERRIDE { return KAsync::null<void>(); }
31
32public Q_SLOTS:
33 void open() Q_DECL_OVERRIDE {}
34 void close() Q_DECL_OVERRIDE {}
35};
36
37class TestResourceFacade : public Akonadi2::GenericFacade<Akonadi2::ApplicationDomain::Event>
38{
39public:
40 TestResourceFacade(const QByteArray &instanceIdentifier, const QSharedPointer<EntityStorage<Akonadi2::ApplicationDomain::Event> > storage, const QSharedPointer<Akonadi2::ResourceAccessInterface> resourceAccess)
41 : Akonadi2::GenericFacade<Akonadi2::ApplicationDomain::Event>(instanceIdentifier, QSharedPointer<TestEventAdaptorFactory>::create(), storage, resourceAccess)
42 {
43
44 }
45 virtual ~TestResourceFacade()
46 {
47
48 }
49};
50
51class GenericFacadeBenchmark : public QObject
52{
53 Q_OBJECT
54private Q_SLOTS:
55
56 void initTestCase()
57 {
58 Akonadi2::Storage store(Akonadi2::storageLocation(), "identifier", Akonadi2::Storage::ReadWrite);
59 store.removeFromDisk();
60 }
61
62 void testLoad()
63 {
64 const QByteArray identifier = "identifier";
65 const int count = 100000;
66
67 //Setup
68 auto domainTypeAdaptorFactory = QSharedPointer<TestEventAdaptorFactory>::create();
69 {
70 Akonadi2::Storage storage(Akonadi2::storageLocation(), identifier, Akonadi2::Storage::ReadWrite);
71 auto transaction = storage.createTransaction(Akonadi2::Storage::ReadWrite);
72 auto db = transaction.openDatabase();
73 for (int i = 0; i < count; i++) {
74 auto domainObject = Akonadi2::ApplicationDomain::Event::Ptr::create();
75 domainObject->setProperty("uid", "uid");
76 domainObject->setProperty("summary", "summary");
77
78 flatbuffers::FlatBufferBuilder fbb;
79 domainTypeAdaptorFactory->createBuffer(*domainObject, fbb);
80 db.write(QString::number(i).toLatin1(), QByteArray::fromRawData(reinterpret_cast<const char*>(fbb.GetBufferPointer()), fbb.GetSize()));
81 }
82 }
83
84 Akonadi2::Query query;
85 query.liveQuery = false;
86
87 //Benchmark
88 QBENCHMARK {
89 auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create();
90 auto resourceAccess = QSharedPointer<TestResourceAccess>::create();
91 auto storage = QSharedPointer<EntityStorage<Akonadi2::ApplicationDomain::Event> >::create("identifier", domainTypeAdaptorFactory, "bufferType");
92 TestResourceFacade facade(identifier, storage, resourceAccess);
93
94 async::SyncListResult<Akonadi2::ApplicationDomain::Event::Ptr> result(resultSet->emitter());
95
96 facade.load(query, resultSet).exec().waitForFinished();
97 resultSet->initialResultSetComplete();
98
99 //We have to wait for the events that deliver the results to be processed by the eventloop
100 result.exec();
101
102 QCOMPARE(result.size(), count);
103 }
104
105 // Print memory layout, RSS is what is in memory
106 // std::system("exec pmap -x \"$PPID\"");
107 }
108};
109
110QTEST_MAIN(GenericFacadeBenchmark)
111#include "genericfacadebenchmark.moc"
diff --git a/tests/genericfacadetest.cpp b/tests/genericfacadetest.cpp
index 7aaec23..45ca54d 100644
--- a/tests/genericfacadetest.cpp
+++ b/tests/genericfacadetest.cpp
@@ -74,7 +74,7 @@ private Q_SLOTS:
74 query.liveQuery = false; 74 query.liveQuery = false;
75 75
76 auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create(); 76 auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create();
77 auto storage = QSharedPointer<TestEntityStorage>::create("identifier", QSharedPointer<TestEventAdaptorFactory>::create()); 77 auto storage = QSharedPointer<TestEntityStorage>::create("identifier", QSharedPointer<TestEventAdaptorFactory>::create(), "bufferType");
78 auto resourceAccess = QSharedPointer<TestResourceAccess>::create(); 78 auto resourceAccess = QSharedPointer<TestResourceAccess>::create();
79 storage->mResults << Akonadi2::ApplicationDomain::Event::Ptr::create(); 79 storage->mResults << Akonadi2::ApplicationDomain::Event::Ptr::create();
80 TestResourceFacade facade("identifier", storage, resourceAccess); 80 TestResourceFacade facade("identifier", storage, resourceAccess);
@@ -96,7 +96,7 @@ private Q_SLOTS:
96 query.liveQuery = true; 96 query.liveQuery = true;
97 97
98 auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create(); 98 auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create();
99 auto storage = QSharedPointer<TestEntityStorage>::create("identifier", QSharedPointer<TestEventAdaptorFactory>::create()); 99 auto storage = QSharedPointer<TestEntityStorage>::create("identifier", QSharedPointer<TestEventAdaptorFactory>::create(), "bufferType");
100 auto resourceAccess = QSharedPointer<TestResourceAccess>::create(); 100 auto resourceAccess = QSharedPointer<TestResourceAccess>::create();
101 storage->mResults << Akonadi2::ApplicationDomain::Event::Ptr::create(); 101 storage->mResults << Akonadi2::ApplicationDomain::Event::Ptr::create();
102 TestResourceFacade facade("identifier", storage, resourceAccess); 102 TestResourceFacade facade("identifier", storage, resourceAccess);