summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rw-r--r--client/main.cpp2
-rw-r--r--common/CMakeLists.txt1
-rw-r--r--common/clientapi.cpp8
-rw-r--r--common/clientapi.h97
-rw-r--r--common/domain/event.fbs2
-rw-r--r--common/domainadaptor.cpp63
-rw-r--r--common/domainadaptor.h187
-rw-r--r--common/entitybuffer.cpp27
-rw-r--r--common/entitybuffer.h26
-rw-r--r--common/facade.h88
-rw-r--r--common/resource.h7
-rw-r--r--common/resourceaccess.cpp10
-rw-r--r--common/resourceaccess.h4
-rw-r--r--common/storage.h2
-rw-r--r--common/storage_common.cpp2
-rw-r--r--common/storage_lmdb.cpp4
-rw-r--r--common/test/clientapitest.cpp18
-rw-r--r--dummyresource/domainadaptor.cpp135
-rw-r--r--dummyresource/domainadaptor.h6
-rw-r--r--dummyresource/facade.cpp89
-rw-r--r--dummyresource/facade.h23
-rw-r--r--dummyresource/resourcefactory.cpp48
-rw-r--r--synchronizer/listener.cpp38
-rw-r--r--synchronizer/listener.h4
-rw-r--r--tests/CMakeLists.txt15
-rw-r--r--tests/domainadaptortest.cpp100
-rw-r--r--tests/dummyresourcebenchmark.cpp94
-rw-r--r--tests/dummyresourcetest.cpp20
29 files changed, 669 insertions, 455 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 97c4466..bc16184 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,12 +36,14 @@ function(generate_flatbuffers)
36endfunction(generate_flatbuffers) 36endfunction(generate_flatbuffers)
37 37
38set(CMAKE_AUTOMOC ON) 38set(CMAKE_AUTOMOC ON)
39add_definitions("-Wall -std=c++0x") 39add_definitions("-Wall -std=c++0x -g")
40include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${FLATBUFFERS_INCLUDE_DIR} ${CMAKE_BINARY_DIR}/common) 40include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${FLATBUFFERS_INCLUDE_DIR} ${CMAKE_BINARY_DIR}/common)
41include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/common) 41include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/common)
42 42
43configure_file(hawd.conf hawd.conf) 43configure_file(hawd.conf hawd.conf)
44 44
45enable_testing()
46
45set(AKONADI2_RESOURCE_PLUGINS_PATH ${QT_PLUGIN_INSTALL_DIR}/akonadi2/resources) 47set(AKONADI2_RESOURCE_PLUGINS_PATH ${QT_PLUGIN_INSTALL_DIR}/akonadi2/resources)
46 48
47# common, eventually a lib but right now just the command buffers 49# common, eventually a lib but right now just the command buffers
diff --git a/client/main.cpp b/client/main.cpp
index 61c1c1a..b4cb081 100644
--- a/client/main.cpp
+++ b/client/main.cpp
@@ -41,7 +41,7 @@ int main(int argc, char *argv[])
41 } 41 }
42 42
43 for (const QString &resource: resources) { 43 for (const QString &resource: resources) {
44 Akonadi2::ResourceAccess *resAccess = new Akonadi2::ResourceAccess(resource); 44 Akonadi2::ResourceAccess *resAccess = new Akonadi2::ResourceAccess(resource.toLatin1());
45 QObject::connect(&app, &QCoreApplication::aboutToQuit, 45 QObject::connect(&app, &QCoreApplication::aboutToQuit,
46 resAccess, &Akonadi2::ResourceAccess::close); 46 resAccess, &Akonadi2::ResourceAccess::close);
47 resAccess->sendCommand(Akonadi2::Commands::SynchronizeCommand); 47 resAccess->sendCommand(Akonadi2::Commands::SynchronizeCommand);
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 650691c..b06718f 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -30,6 +30,7 @@ set(command_SRCS
30 commands.cpp 30 commands.cpp
31 console.cpp 31 console.cpp
32 pipeline.cpp 32 pipeline.cpp
33 domainadaptor.cpp
33 resource.cpp 34 resource.cpp
34 resourceaccess.cpp 35 resourceaccess.cpp
35 storage_common.cpp 36 storage_common.cpp
diff --git a/common/clientapi.cpp b/common/clientapi.cpp
index 6f0b421..48de1b0 100644
--- a/common/clientapi.cpp
+++ b/common/clientapi.cpp
@@ -14,24 +14,24 @@ namespace async
14namespace Akonadi2 14namespace Akonadi2
15{ 15{
16 16
17namespace Domain 17namespace ApplicationDomain
18{ 18{
19 19
20template<> 20template<>
21QString getTypeName<Event>() 21QByteArray getTypeName<Event>()
22{ 22{
23 return "event"; 23 return "event";
24} 24}
25 25
26template<> 26template<>
27QString getTypeName<Todo>() 27QByteArray getTypeName<Todo>()
28{ 28{
29 return "todo"; 29 return "todo";
30} 30}
31 31
32} // namespace Domain 32} // namespace Domain
33 33
34void Store::shutdown(const QString &identifier) 34void Store::shutdown(const QByteArray &identifier)
35{ 35{
36 Akonadi2::ResourceAccess resourceAccess(identifier); 36 Akonadi2::ResourceAccess resourceAccess(identifier);
37 //FIXME this starts the resource, just to shut it down again if it's not running in the first place. 37 //FIXME this starts the resource, just to shut it down again if it's not running in the first place.
diff --git a/common/clientapi.h b/common/clientapi.h
index 63305ab..22448b3 100644
--- a/common/clientapi.h
+++ b/common/clientapi.h
@@ -163,7 +163,7 @@ namespace async {
163namespace Akonadi2 { 163namespace Akonadi2 {
164 164
165/** 165/**
166 * Standardized Domain Types 166 * Standardized Application Domain Types
167 * 167 *
168 * They don't adhere to any standard and can be freely extended 168 * They don't adhere to any standard and can be freely extended
169 * Their sole purpose is providing a standardized interface to access data. 169 * Their sole purpose is providing a standardized interface to access data.
@@ -172,7 +172,7 @@ namespace Akonadi2 {
172 * 172 *
173 * These types will be frequently modified (for every new feature that should be exposed to the any client) 173 * These types will be frequently modified (for every new feature that should be exposed to the any client)
174 */ 174 */
175namespace Domain { 175namespace ApplicationDomain {
176 176
177/** 177/**
178 * This class has to be implemented by resources and can be used as generic interface to access the buffer properties 178 * This class has to be implemented by resources and can be used as generic interface to access the buffer properties
@@ -180,9 +180,9 @@ namespace Domain {
180class BufferAdaptor { 180class BufferAdaptor {
181public: 181public:
182 virtual ~BufferAdaptor() {} 182 virtual ~BufferAdaptor() {}
183 virtual QVariant getProperty(const QString &key) const { return QVariant(); } 183 virtual QVariant getProperty(const QByteArray &key) const { return QVariant(); }
184 virtual void setProperty(const QString &key, const QVariant &value) {} 184 virtual void setProperty(const QByteArray &key, const QVariant &value) {}
185 virtual QStringList availableProperties() const { return QStringList(); } 185 virtual QList<QByteArray> availableProperties() const { return QList<QByteArray>(); }
186}; 186};
187 187
188class MemoryBufferAdaptor : public BufferAdaptor { 188class MemoryBufferAdaptor : public BufferAdaptor {
@@ -202,12 +202,12 @@ public:
202 202
203 virtual ~MemoryBufferAdaptor() {} 203 virtual ~MemoryBufferAdaptor() {}
204 204
205 virtual QVariant getProperty(const QString &key) const { return mValues.value(key); } 205 virtual QVariant getProperty(const QByteArray &key) const { return mValues.value(key); }
206 virtual void setProperty(const QString &key, const QVariant &value) { mValues.insert(key, value); } 206 virtual void setProperty(const QByteArray &key, const QVariant &value) { mValues.insert(key, value); }
207 virtual QStringList availableProperties() const { return mValues.keys(); } 207 virtual QByteArrayList availableProperties() const { return mValues.keys(); }
208 208
209private: 209private:
210 QHash<QString, QVariant> mValues; 210 QHash<QByteArray, QVariant> mValues;
211}; 211};
212 212
213/** 213/**
@@ -215,14 +215,14 @@ private:
215 * * provide a unified interface to read buffers (for zero-copy reading) 215 * * provide a unified interface to read buffers (for zero-copy reading)
216 * * record changes to generate changesets for modifications 216 * * record changes to generate changesets for modifications
217 */ 217 */
218class AkonadiDomainType { 218class ApplicationDomainType {
219public: 219public:
220 AkonadiDomainType() 220 ApplicationDomainType()
221 :mAdaptor(new MemoryBufferAdaptor()) 221 :mAdaptor(new MemoryBufferAdaptor())
222 { 222 {
223 223
224 } 224 }
225 AkonadiDomainType(const QString &resourceName, const QString &identifier, qint64 revision, const QSharedPointer<BufferAdaptor> &adaptor) 225 ApplicationDomainType(const QByteArray &resourceName, const QByteArray &identifier, qint64 revision, const QSharedPointer<BufferAdaptor> &adaptor)
226 : mAdaptor(adaptor), 226 : mAdaptor(adaptor),
227 mResourceName(resourceName), 227 mResourceName(resourceName),
228 mIdentifier(identifier), 228 mIdentifier(identifier),
@@ -230,41 +230,43 @@ public:
230 { 230 {
231 } 231 }
232 232
233 virtual ~AkonadiDomainType() {} 233 virtual ~ApplicationDomainType() {}
234 234
235 virtual QVariant getProperty(const QString &key) const { return mAdaptor->getProperty(key); } 235 virtual QVariant getProperty(const QByteArray &key) const { return mAdaptor->getProperty(key); }
236 virtual void setProperty(const QString &key, const QVariant &value){ mChangeSet.insert(key, value); mAdaptor->setProperty(key, value); } 236 virtual void setProperty(const QByteArray &key, const QVariant &value){ mChangeSet.insert(key, value); mAdaptor->setProperty(key, value); }
237 virtual QByteArrayList changedProperties() const { return mChangeSet.keys(); }
238 qint64 revision() const { return mRevision; }
237 239
238private: 240private:
239 QSharedPointer<BufferAdaptor> mAdaptor; 241 QSharedPointer<BufferAdaptor> mAdaptor;
240 QHash<QString, QVariant> mChangeSet; 242 QHash<QByteArray, QVariant> mChangeSet;
241 /* 243 /*
242 * Each domain object needs to store the resource, identifier, revision triple so we can link back to the storage location. 244 * Each domain object needs to store the resource, identifier, revision triple so we can link back to the storage location.
243 */ 245 */
244 QString mResourceName; 246 QString mResourceName;
245 QString mIdentifier; 247 QByteArray mIdentifier;
246 qint64 mRevision; 248 qint64 mRevision;
247}; 249};
248 250
249struct Event : public AkonadiDomainType { 251struct Event : public ApplicationDomainType {
250 typedef QSharedPointer<Event> Ptr; 252 typedef QSharedPointer<Event> Ptr;
251 using AkonadiDomainType::AkonadiDomainType; 253 using ApplicationDomainType::ApplicationDomainType;
252}; 254};
253 255
254struct Todo : public AkonadiDomainType { 256struct Todo : public ApplicationDomainType {
255 typedef QSharedPointer<Todo> Ptr; 257 typedef QSharedPointer<Todo> Ptr;
256 using AkonadiDomainType::AkonadiDomainType; 258 using ApplicationDomainType::ApplicationDomainType;
257}; 259};
258 260
259struct Calendar : public AkonadiDomainType { 261struct Calendar : public ApplicationDomainType {
260 typedef QSharedPointer<Calendar> Ptr; 262 typedef QSharedPointer<Calendar> Ptr;
261 using AkonadiDomainType::AkonadiDomainType; 263 using ApplicationDomainType::ApplicationDomainType;
262}; 264};
263 265
264class Mail : public AkonadiDomainType { 266class Mail : public ApplicationDomainType {
265}; 267};
266 268
267class Folder : public AkonadiDomainType { 269class Folder : public ApplicationDomainType {
268}; 270};
269 271
270/** 272/**
@@ -274,13 +276,13 @@ class Folder : public AkonadiDomainType {
274 */ 276 */
275 277
276template<class DomainType> 278template<class DomainType>
277QString getTypeName(); 279QByteArray getTypeName();
278 280
279template<> 281template<>
280QString getTypeName<Event>(); 282QByteArray getTypeName<Event>();
281 283
282template<> 284template<>
283QString getTypeName<Todo>(); 285QByteArray getTypeName<Todo>();
284 286
285} 287}
286 288
@@ -298,19 +300,22 @@ using namespace async;
298 * * what resources to search 300 * * what resources to search
299 * * filters on various properties (parent collection, startDate range, ....) 301 * * filters on various properties (parent collection, startDate range, ....)
300 * * properties we need (for on-demand querying) 302 * * properties we need (for on-demand querying)
303 *
304 * syncOnDemand: Execute a source sync before executing the query
305 * processAll: Ensure all local messages are processed before querying to guarantee an up-to date dataset.
301 */ 306 */
302class Query 307class Query
303{ 308{
304public: 309public:
305 Query() : syncOnDemand(true), processAll(false) {} 310 Query() : syncOnDemand(true), processAll(false) {}
306 //Could also be a propertyFilter 311 //Could also be a propertyFilter
307 QStringList resources; 312 QByteArrayList resources;
308 //Could also be a propertyFilter 313 //Could also be a propertyFilter
309 QStringList ids; 314 QByteArrayList ids;
310 //Filters to apply 315 //Filters to apply
311 QHash<QString, QVariant> propertyFilter; 316 QHash<QByteArray, QVariant> propertyFilter;
312 //Properties to retrieve 317 //Properties to retrieve
313 QSet<QString> requestedProperties; 318 QSet<QByteArray> requestedProperties;
314 bool syncOnDemand; 319 bool syncOnDemand;
315 bool processAll; 320 bool processAll;
316}; 321};
@@ -328,7 +333,7 @@ template<class DomainType>
328class StoreFacade { 333class StoreFacade {
329public: 334public:
330 virtual ~StoreFacade(){}; 335 virtual ~StoreFacade(){};
331 QString type() const { return Domain::getTypeName<DomainType>(); } 336 QByteArray type() const { return ApplicationDomain::getTypeName<DomainType>(); }
332 virtual Async::Job<void> create(const DomainType &domainObject) = 0; 337 virtual Async::Job<void> create(const DomainType &domainObject) = 0;
333 virtual Async::Job<void> modify(const DomainType &domainObject) = 0; 338 virtual Async::Job<void> modify(const DomainType &domainObject) = 0;
334 virtual Async::Job<void> remove(const DomainType &domainObject) = 0; 339 virtual Async::Job<void> remove(const DomainType &domainObject) = 0;
@@ -351,15 +356,15 @@ public:
351 return factory; 356 return factory;
352 } 357 }
353 358
354 static QString key(const QString &resource, const QString &type) 359 static QByteArray key(const QByteArray &resource, const QByteArray &type)
355 { 360 {
356 return resource + type; 361 return resource + type;
357 } 362 }
358 363
359 template<class DomainType, class Facade> 364 template<class DomainType, class Facade>
360 void registerFacade(const QString &resource) 365 void registerFacade(const QByteArray &resource)
361 { 366 {
362 const QString typeName = Domain::getTypeName<DomainType>(); 367 const QByteArray typeName = ApplicationDomain::getTypeName<DomainType>();
363 mFacadeRegistry.insert(key(resource, typeName), [](){ return new Facade; }); 368 mFacadeRegistry.insert(key(resource, typeName), [](){ return new Facade; });
364 } 369 }
365 370
@@ -373,16 +378,16 @@ public:
373 * FIXME the factory function should really be returning QSharedPointer<void>, which doesn't work (std::shared_pointer<void> would though). That way i.e. a test could keep the object alive until it's done. 378 * FIXME the factory function should really be returning QSharedPointer<void>, which doesn't work (std::shared_pointer<void> would though). That way i.e. a test could keep the object alive until it's done.
374 */ 379 */
375 template<class DomainType, class Facade> 380 template<class DomainType, class Facade>
376 void registerFacade(const QString &resource, const std::function<void*(void)> &customFactoryFunction) 381 void registerFacade(const QByteArray &resource, const std::function<void*(void)> &customFactoryFunction)
377 { 382 {
378 const QString typeName = Domain::getTypeName<DomainType>(); 383 const QByteArray typeName = ApplicationDomain::getTypeName<DomainType>();
379 mFacadeRegistry.insert(key(resource, typeName), customFactoryFunction); 384 mFacadeRegistry.insert(key(resource, typeName), customFactoryFunction);
380 } 385 }
381 386
382 template<class DomainType> 387 template<class DomainType>
383 QSharedPointer<StoreFacade<DomainType> > getFacade(const QString &resource) 388 QSharedPointer<StoreFacade<DomainType> > getFacade(const QByteArray &resource)
384 { 389 {
385 const QString typeName = Domain::getTypeName<DomainType>(); 390 const QByteArray typeName = ApplicationDomain::getTypeName<DomainType>();
386 auto factoryFunction = mFacadeRegistry.value(key(resource, typeName)); 391 auto factoryFunction = mFacadeRegistry.value(key(resource, typeName));
387 if (factoryFunction) { 392 if (factoryFunction) {
388 return QSharedPointer<StoreFacade<DomainType> >(static_cast<StoreFacade<DomainType>* >(factoryFunction())); 393 return QSharedPointer<StoreFacade<DomainType> >(static_cast<StoreFacade<DomainType>* >(factoryFunction()));
@@ -392,7 +397,7 @@ public:
392 } 397 }
393 398
394private: 399private:
395 QHash<QString, std::function<void*(void)> > mFacadeRegistry; 400 QHash<QByteArray, std::function<void*(void)> > mFacadeRegistry;
396}; 401};
397 402
398/** 403/**
@@ -421,7 +426,7 @@ public:
421 // query tells us in which resources we're interested 426 // query tells us in which resources we're interested
422 // TODO: queries to individual resources could be parallelized 427 // TODO: queries to individual resources could be parallelized
423 Async::Job<void> job = Async::null<void>(); 428 Async::Job<void> job = Async::null<void>();
424 for(const QString &resource : query.resources) { 429 for(const QByteArray &resource : query.resources) {
425 auto facade = FacadeFactory::instance().getFacade<DomainType>(resource); 430 auto facade = FacadeFactory::instance().getFacade<DomainType>(resource);
426 //We have to bind an instance to the function callback. Since we use a shared pointer this keeps the result provider instance (and thus also the emitter) alive. 431 //We have to bind an instance to the function callback. Since we use a shared pointer this keeps the result provider instance (and thus also the emitter) alive.
427 std::function<void(const typename DomainType::Ptr &)> addCallback = std::bind(&ResultProvider<typename DomainType::Ptr>::add, resultSet, std::placeholders::_1); 432 std::function<void(const typename DomainType::Ptr &)> addCallback = std::bind(&ResultProvider<typename DomainType::Ptr>::add, resultSet, std::placeholders::_1);
@@ -460,7 +465,7 @@ public:
460 */ 465 */
461 //TODO return job that tracks progress until resource has stored the message in it's queue? 466 //TODO return job that tracks progress until resource has stored the message in it's queue?
462 template <class DomainType> 467 template <class DomainType>
463 static void create(const DomainType &domainObject, const QString &resourceIdentifier) { 468 static void create(const DomainType &domainObject, const QByteArray &resourceIdentifier) {
464 //Potentially move to separate thread as well 469 //Potentially move to separate thread as well
465 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceIdentifier); 470 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceIdentifier);
466 auto job = facade->create(domainObject); 471 auto job = facade->create(domainObject);
@@ -475,7 +480,7 @@ public:
475 * This includes moving etc. since these are also simple settings on a property. 480 * This includes moving etc. since these are also simple settings on a property.
476 */ 481 */
477 template <class DomainType> 482 template <class DomainType>
478 static void modify(const DomainType &domainObject, const QString &resourceIdentifier) { 483 static void modify(const DomainType &domainObject, const QByteArray &resourceIdentifier) {
479 //Potentially move to separate thread as well 484 //Potentially move to separate thread as well
480 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceIdentifier); 485 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceIdentifier);
481 facade.modify(domainObject); 486 facade.modify(domainObject);
@@ -485,13 +490,13 @@ public:
485 * Remove an entity. 490 * Remove an entity.
486 */ 491 */
487 template <class DomainType> 492 template <class DomainType>
488 static void remove(const DomainType &domainObject, const QString &resourceIdentifier) { 493 static void remove(const DomainType &domainObject, const QByteArray &resourceIdentifier) {
489 //Potentially move to separate thread as well 494 //Potentially move to separate thread as well
490 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceIdentifier); 495 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceIdentifier);
491 facade.remove(domainObject); 496 facade.remove(domainObject);
492 } 497 }
493 498
494 static void shutdown(const QString &resourceIdentifier); 499 static void shutdown(const QByteArray &resourceIdentifier);
495}; 500};
496 501
497} 502}
diff --git a/common/domain/event.fbs b/common/domain/event.fbs
index 49ff270..0c6628b 100644
--- a/common/domain/event.fbs
+++ b/common/domain/event.fbs
@@ -1,4 +1,4 @@
1namespace Akonadi2.Domain.Buffer; 1namespace Akonadi2.ApplicationDomain.Buffer;
2 2
3table Event { 3table Event {
4 uid:string; 4 uid:string;
diff --git a/common/domainadaptor.cpp b/common/domainadaptor.cpp
new file mode 100644
index 0000000..aa8c3d9
--- /dev/null
+++ b/common/domainadaptor.cpp
@@ -0,0 +1,63 @@
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
20#include "domainadaptor.h"
21
22template <>
23flatbuffers::uoffset_t extractProperty<QString>(const QVariant &property, flatbuffers::FlatBufferBuilder &fbb)
24{
25 if (property.isValid()) {
26 return fbb.CreateString(property.toString().toStdString()).o;
27 }
28 return 0;
29}
30
31template <>
32QSharedPointer<ReadPropertyMapper<Akonadi2::ApplicationDomain::Buffer::Event> > initializeReadPropertyMapper<Akonadi2::ApplicationDomain::Buffer::Event>()
33{
34 auto propertyMapper = QSharedPointer<ReadPropertyMapper<Akonadi2::ApplicationDomain::Buffer::Event> >::create();
35 propertyMapper->addMapping("summary", [](Akonadi2::ApplicationDomain::Buffer::Event const *buffer) -> QVariant {
36 if (buffer->summary()) {
37 return QString::fromStdString(buffer->summary()->c_str());
38 }
39 return QVariant();
40 });
41 propertyMapper->addMapping("uid", [](Akonadi2::ApplicationDomain::Buffer::Event const *buffer) -> QVariant {
42 if (buffer->uid()) {
43 return QString::fromStdString(buffer->uid()->c_str());
44 }
45 return QVariant();
46 });
47 return propertyMapper;
48}
49
50template <>
51QSharedPointer<WritePropertyMapper<Akonadi2::ApplicationDomain::Buffer::EventBuilder> > initializeWritePropertyMapper<Akonadi2::ApplicationDomain::Buffer::EventBuilder>()
52{
53 auto propertyMapper = QSharedPointer<WritePropertyMapper<Akonadi2::ApplicationDomain::Buffer::EventBuilder> >::create();
54 propertyMapper->addMapping("summary", [](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(Akonadi2::ApplicationDomain::Buffer::EventBuilder &)> {
55 auto offset = extractProperty<QString>(value, fbb);
56 return [offset](Akonadi2::ApplicationDomain::Buffer::EventBuilder &builder) { builder.add_summary(offset); };
57 });
58 propertyMapper->addMapping("uid", [](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(Akonadi2::ApplicationDomain::Buffer::EventBuilder &)> {
59 auto offset = extractProperty<QString>(value, fbb);
60 return [offset](Akonadi2::ApplicationDomain::Buffer::EventBuilder &builder) { builder.add_uid(offset); };
61 });
62 return propertyMapper;
63}
diff --git a/common/domainadaptor.h b/common/domainadaptor.h
index e46e893..e356692 100644
--- a/common/domainadaptor.h
+++ b/common/domainadaptor.h
@@ -21,59 +21,194 @@
21 21
22#include "entity_generated.h" 22#include "entity_generated.h"
23#include <QVariant> 23#include <QVariant>
24#include <QString> 24#include <QByteArray>
25#include <functional> 25#include <functional>
26#include "clientapi.h" //for domain parts 26#include "clientapi.h" //for domain parts
27 27
28#include "event_generated.h"
29#include "entity_generated.h"
30#include "metadata_generated.h"
31#include "entitybuffer.h"
32
28/** 33/**
29 * The property mapper holds accessor functions for all properties. 34 * The property mapper is a non-typesafe virtual dispatch.
30 * 35 *
31 * It is by default initialized with accessors that access the local-only buffer, 36 * Instead of using an interface and requring each implementation to override
32 * and resource simply have to overwrite those accessors. 37 * a virtual method per property, the property mapper can be filled with accessors
38 * that extract the properties from resource types.
33 */ 39 */
34template<typename BufferType> 40template<typename BufferType>
35class PropertyMapper 41class ReadPropertyMapper
42{
43public:
44 virtual QVariant getProperty(const QByteArray &key, BufferType const *buffer) const
45 {
46 if (mReadAccessors.contains(key)) {
47 auto accessor = mReadAccessors.value(key);
48 return accessor(buffer);
49 }
50 return QVariant();
51 }
52 bool hasMapping(const QByteArray &key) const { return mReadAccessors.contains(key); }
53 QList<QByteArray> availableProperties() const { return mReadAccessors.keys(); }
54 void addMapping(const QByteArray &property, const std::function<QVariant(BufferType const *)> &mapping) {
55 mReadAccessors.insert(property, mapping);
56 }
57private:
58 QHash<QByteArray, std::function<QVariant(BufferType const *)> > mReadAccessors;
59};
60
61template<typename BufferBuilder>
62class WritePropertyMapper
36{ 63{
37public: 64public:
38 void setProperty(const QString &key, const QVariant &value, BufferType *buffer) 65 virtual void setProperty(const QByteArray &key, const QVariant &value, QList<std::function<void(BufferBuilder &)> > &builderCalls, flatbuffers::FlatBufferBuilder &fbb) const
39 { 66 {
40 if (mWriteAccessors.contains(key)) { 67 if (mWriteAccessors.contains(key)) {
41 auto accessor = mWriteAccessors.value(key); 68 auto accessor = mWriteAccessors.value(key);
42 return accessor(value, buffer); 69 builderCalls << accessor(value, fbb);
43 } 70 }
44 } 71 }
72 bool hasMapping(const QByteArray &key) const { return mWriteAccessors.contains(key); }
73 void addMapping(const QByteArray &property, const std::function<std::function<void(BufferBuilder &)>(const QVariant &, flatbuffers::FlatBufferBuilder &)> &mapping) {
74 mWriteAccessors.insert(property, mapping);
75 }
76private:
77 QHash<QByteArray, std::function<std::function<void(BufferBuilder &)>(const QVariant &, flatbuffers::FlatBufferBuilder &)> > mWriteAccessors;
78};
79
80/**
81 * Defines how to convert qt primitives to flatbuffer ones
82 * TODO: rename to createProperty or so?
83 */
84template <class T>
85flatbuffers::uoffset_t extractProperty(const QVariant &, flatbuffers::FlatBufferBuilder &fbb);
86
87/**
88 * Create a buffer from a domain object using the provided mappings
89 */
90template <class Builder, class Buffer>
91flatbuffers::Offset<Buffer> createBufferPart(const Akonadi2::ApplicationDomain::ApplicationDomainType &domainObject, flatbuffers::FlatBufferBuilder &fbb, const WritePropertyMapper<Builder> &mapper)
92{
93 //First create a primitives such as strings using the mappings
94 QList<std::function<void(Builder &)> > propertiesToAddToResource;
95 for (const auto &property : domainObject.changedProperties()) {
96 qWarning() << "copying property " << property;
97 const auto value = domainObject.getProperty(property);
98 if (mapper.hasMapping(property)) {
99 mapper.setProperty(property, domainObject.getProperty(property), propertiesToAddToResource, fbb);
100 } else {
101 qWarning() << "no mapping for property available " << property;
102 }
103 }
104
105 //Then create all porperties using the above generated builderCalls
106 Builder builder(fbb);
107 for (auto propertyBuilder : propertiesToAddToResource) {
108 propertyBuilder(builder);
109 }
110 return builder.Finish();
111}
112
113/**
114 * A generic adaptor implementation that uses a property mapper to read/write values.
115 *
116 * TODO: this is the read-only part. Create a write only equivalent
117 */
118template <class LocalBuffer, class ResourceBuffer>
119class GenericBufferAdaptor : public Akonadi2::ApplicationDomain::BufferAdaptor
120{
121public:
122 GenericBufferAdaptor()
123 : BufferAdaptor()
124 {
125
126 }
45 127
46 virtual QVariant getProperty(const QString &key, BufferType const *buffer) const 128 //TODO remove
129 void setProperty(const QByteArray &key, const QVariant &value)
47 { 130 {
48 if (mReadAccessors.contains(key)) { 131 }
49 auto accessor = mReadAccessors.value(key); 132
50 return accessor(buffer); 133 virtual QVariant getProperty(const QByteArray &key) const
134 {
135 if (mResourceBuffer && mResourceMapper->hasMapping(key)) {
136 return mResourceMapper->getProperty(key, mResourceBuffer);
137 } else if (mLocalBuffer && mLocalMapper->hasMapping(key)) {
138 return mLocalMapper->getProperty(key, mLocalBuffer);
51 } 139 }
140 qWarning() << "no mapping available for key " << key;
52 return QVariant(); 141 return QVariant();
53 } 142 }
54 QHash<QString, std::function<QVariant(BufferType const *)> > mReadAccessors; 143
55 QHash<QString, std::function<void(const QVariant &, BufferType*)> > mWriteAccessors; 144 virtual QList<QByteArray> availableProperties() const
145 {
146 QList<QByteArray> props;
147 props << mResourceMapper->availableProperties();
148 props << mLocalMapper->availableProperties();
149 return props;
150 }
151
152 LocalBuffer const *mLocalBuffer;
153 ResourceBuffer const *mResourceBuffer;
154 QSharedPointer<ReadPropertyMapper<LocalBuffer> > mLocalMapper;
155 QSharedPointer<ReadPropertyMapper<ResourceBuffer> > mResourceMapper;
56}; 156};
57 157
58//The factory should define how to go from an entitybuffer (local + resource buffer), to a domain type adapter. 158/**
59//It defines how values are split accross local and resource buffer. 159 * Initializes the local property mapper.
60//This is required by the facade the read the value, and by the pipeline preprocessors to access the domain values in a generic way. 160 *
61// template<typename DomainType, typename LocalBuffer, typename ResourceBuffer> 161 * Provide an implementation for each application domain type.
62// class DomainTypeAdaptorFactory 162 */
63// { 163template <class T>
64// }; 164QSharedPointer<ReadPropertyMapper<T> > initializeReadPropertyMapper();
165
166template <class T>
167QSharedPointer<WritePropertyMapper<T> > initializeWritePropertyMapper();
65 168
66template<typename DomainType, typename LocalBuffer, typename ResourceBuffer> 169/**
67class DomainTypeAdaptorFactory/* <typename DomainType, LocalBuffer, ResourceBuffer> */ 170 * The factory should define how to go from an entitybuffer (local + resource buffer), to a domain type adapter.
171 * It defines how values are split accross local and resource buffer.
172 * This is required by the facade the read the value, and by the pipeline preprocessors to access the domain values in a generic way.
173 */
174template<typename DomainType, typename LocalBuffer, typename ResourceBuffer, typename LocalBuilder, typename ResourceBuilder>
175class DomainTypeAdaptorFactory
68{ 176{
69public: 177public:
178 DomainTypeAdaptorFactory() :
179 mLocalMapper(initializeReadPropertyMapper<LocalBuffer>()),
180 mResourceMapper(QSharedPointer<ReadPropertyMapper<ResourceBuffer> >::create()),
181 mLocalWriteMapper(initializeWritePropertyMapper<LocalBuilder>()),
182 mResourceWriteMapper(QSharedPointer<WritePropertyMapper<ResourceBuilder> >::create())
183 {};
70 virtual ~DomainTypeAdaptorFactory() {}; 184 virtual ~DomainTypeAdaptorFactory() {};
71 virtual QSharedPointer<Akonadi2::Domain::BufferAdaptor> createAdaptor(const Akonadi2::Entity &entity) = 0; 185
72 virtual void createBuffer(const Akonadi2::Domain::Event &event, flatbuffers::FlatBufferBuilder &fbb) {}; 186 /**
187 * Creates an adaptor for the given domain and resource types.
188 *
189 * This returns by default a GenericBufferAdaptor initialized with the corresponding property mappers.
190 */
191 virtual QSharedPointer<Akonadi2::ApplicationDomain::BufferAdaptor> createAdaptor(const Akonadi2::Entity &entity)
192 {
193 const auto resourceBuffer = Akonadi2::EntityBuffer::readBuffer<ResourceBuffer>(entity.resource());
194 const auto localBuffer = Akonadi2::EntityBuffer::readBuffer<LocalBuffer>(entity.local());
195 // const auto metadataBuffer = Akonadi2::EntityBuffer::readBuffer<Akonadi2::Metadata>(entity.metadata());
196
197 auto adaptor = QSharedPointer<GenericBufferAdaptor<LocalBuffer, ResourceBuffer> >::create();
198 adaptor->mLocalBuffer = localBuffer;
199 adaptor->mLocalMapper = mLocalMapper;
200 adaptor->mResourceBuffer = resourceBuffer;
201 adaptor->mResourceMapper = mResourceMapper;
202 return adaptor;
203 }
204
205 virtual void createBuffer(const Akonadi2::ApplicationDomain::Event &event, flatbuffers::FlatBufferBuilder &fbb) {};
73 206
74protected: 207protected:
75 QSharedPointer<PropertyMapper<LocalBuffer> > mLocalMapper; 208 QSharedPointer<ReadPropertyMapper<LocalBuffer> > mLocalMapper;
76 QSharedPointer<PropertyMapper<ResourceBuffer> > mResourceMapper; 209 QSharedPointer<ReadPropertyMapper<ResourceBuffer> > mResourceMapper;
210 QSharedPointer<WritePropertyMapper<LocalBuilder> > mLocalWriteMapper;
211 QSharedPointer<WritePropertyMapper<ResourceBuilder> > mResourceWriteMapper;
77}; 212};
78 213
79 214
diff --git a/common/entitybuffer.cpp b/common/entitybuffer.cpp
index 5ba4afe..b555ac3 100644
--- a/common/entitybuffer.cpp
+++ b/common/entitybuffer.cpp
@@ -56,17 +56,24 @@ void EntityBuffer::extractResourceBuffer(void *dataValue, int dataSize, const st
56 } 56 }
57} 57}
58 58
59void EntityBuffer::assembleEntityBuffer(flatbuffers::FlatBufferBuilder &fbb, void const *metadataData, size_t metadataSize, void const *resourceData, size_t resourceSize, void const *localData, size_t localSize) 59flatbuffers::Offset<flatbuffers::Vector<uint8_t> > EntityBuffer::appendAsVector(flatbuffers::FlatBufferBuilder &fbb, void const *data, size_t size)
60{ 60{
61 auto metadata = fbb.CreateVector<uint8_t>(static_cast<uint8_t const*>(metadataData), metadataSize); 61 //Since we do memcpy trickery, this will only work on little endian
62 auto resource = fbb.CreateVector<uint8_t>(static_cast<uint8_t const*>(resourceData), resourceSize); 62 assert(FLATBUFFERS_LITTLEENDIAN);
63 auto local = fbb.CreateVector<uint8_t>(static_cast<uint8_t const*>(localData), localSize); 63 auto metadata = fbb.CreateUninitializedVector<uint8_t>(size);
64 auto builder = Akonadi2::EntityBuilder(fbb); 64 {
65 builder.add_metadata(metadata); 65 auto ptr = reinterpret_cast<flatbuffers::Vector<uint8_t> *>(fbb.GetBufferPointer())->Data();
66 builder.add_resource(resource); 66 std::memcpy((void*)ptr, data, size);
67 builder.add_local(local); 67 }
68 return metadata;
69}
68 70
69 auto buffer = builder.Finish(); 71void EntityBuffer::assembleEntityBuffer(flatbuffers::FlatBufferBuilder &fbb, void const *metadataData, size_t metadataSize, void const *resourceData, size_t resourceSize, void const *localData, size_t localSize)
70 Akonadi2::FinishEntityBuffer(fbb, buffer); 72{
73 auto metadata = appendAsVector(fbb, metadataData, metadataSize);
74 auto resource = appendAsVector(fbb, resourceData, resourceSize);
75 auto local = appendAsVector(fbb, localData, localSize);
76 auto entity = Akonadi2::CreateEntity(fbb, metadata, resource, local);
77 Akonadi2::FinishEntityBuffer(fbb, entity);
71} 78}
72 79
diff --git a/common/entitybuffer.h b/common/entitybuffer.h
index 097b450..82152c9 100644
--- a/common/entitybuffer.h
+++ b/common/entitybuffer.h
@@ -15,7 +15,33 @@ public:
15 const Entity &entity(); 15 const Entity &entity();
16 16
17 static void extractResourceBuffer(void *dataValue, int dataSize, const std::function<void(const uint8_t *, size_t size)> &handler); 17 static void extractResourceBuffer(void *dataValue, int dataSize, const std::function<void(const uint8_t *, size_t size)> &handler);
18 /*
19 * TODO: Ideally we would be passing references to vectors in the same bufferbuilder, to avoid needlessly copying data.
20 * Unfortunately I couldn't find a way to cast a table to a vector<uint8_t> reference.
21 * We can't use union's either (which would allow to have a field that stores a selection of tables), as we don't want to modify
22 * the entity schema for each resource's buffers.
23 */
18 static void assembleEntityBuffer(flatbuffers::FlatBufferBuilder &fbb, void const *metadataData, size_t metadataSize, void const *resourceData, size_t resourceSize, void const *localData, size_t localSize); 24 static void assembleEntityBuffer(flatbuffers::FlatBufferBuilder &fbb, void const *metadataData, size_t metadataSize, void const *resourceData, size_t resourceSize, void const *localData, size_t localSize);
25 static flatbuffers::Offset<flatbuffers::Vector<uint8_t> > appendAsVector(flatbuffers::FlatBufferBuilder &fbb, void const *data, size_t size);
26 template<typename T>
27 static const T *readBuffer(const uint8_t *data, int size)
28 {
29 flatbuffers::Verifier verifier(data, size);
30 if (verifier.VerifyBuffer<T>()) {
31 return flatbuffers::GetRoot<T>(data);
32 }
33 return nullptr;
34 }
35
36 template<typename T>
37 static const T *readBuffer(const flatbuffers::Vector<uint8_t> *data)
38 {
39 if (data) {
40 return readBuffer<T>(data->Data(), data->size());
41 }
42 return nullptr;
43 }
44
19 45
20private: 46private:
21 const Entity *mEntity; 47 const Entity *mEntity;
diff --git a/common/facade.h b/common/facade.h
new file mode 100644
index 0000000..98bcb38
--- /dev/null
+++ b/common/facade.h
@@ -0,0 +1,88 @@
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
20#pragma once
21
22#include "clientapi.h"
23
24#include <QByteArray>
25
26#include "async/src/async.h"
27#include "resourceaccess.h"
28#include "commands.h"
29#include "createentity_generated.h"
30#include "domainadaptor.h"
31#include "entitybuffer.h"
32
33namespace Akonadi2 {
34 class ResourceAccess;
35/**
36 * Default facade implementation for resources that are implemented in a separate process using the ResourceAccess class.
37 */
38template <typename DomainType>
39class GenericFacade: public Akonadi2::StoreFacade<DomainType>
40{
41public:
42 GenericFacade(const QByteArray &resourceIdentifier)
43 : Akonadi2::StoreFacade<DomainType>(),
44 mResourceAccess(new ResourceAccess(resourceIdentifier))
45 {
46 }
47
48 ~GenericFacade()
49 {
50 }
51
52protected:
53 Async::Job<void> sendCreateCommand(const QByteArray &t, const QByteArray &buffer)
54 {
55 flatbuffers::FlatBufferBuilder fbb;
56 //This is the resource buffer type and not the domain type
57 auto type = fbb.CreateString(t.constData());
58 auto delta = Akonadi2::EntityBuffer::appendAsVector(fbb, buffer.constData(), buffer.size());
59 auto location = Akonadi2::Commands::CreateCreateEntity(fbb, type, delta);
60 Akonadi2::Commands::FinishCreateEntityBuffer(fbb, location);
61 mResourceAccess->open();
62 return mResourceAccess->sendCommand(Akonadi2::Commands::CreateEntityCommand, fbb);
63 }
64
65 Async::Job<void> synchronizeResource(bool sync, bool processAll)
66 {
67 //TODO check if a sync is necessary
68 //TODO Only sync what was requested
69 //TODO timeout
70 //TODO the synchronization should normally not be necessary: We just return what is already available.
71
72 if (sync || processAll) {
73 return Async::start<void>([=](Async::Future<void> &future) {
74 mResourceAccess->open();
75 mResourceAccess->synchronizeResource(sync, processAll).then<void>([&future]() {
76 future.setFinished();
77 }).exec();
78 });
79 }
80 return Async::null<void>();
81 }
82
83private:
84 //TODO use one resource access instance per application => make static
85 QSharedPointer<Akonadi2::ResourceAccess> mResourceAccess;
86};
87
88}
diff --git a/common/resource.h b/common/resource.h
index bcce229..18a6827 100644
--- a/common/resource.h
+++ b/common/resource.h
@@ -26,10 +26,12 @@
26namespace Akonadi2 26namespace Akonadi2
27{ 27{
28 28
29/**
30 * Resource interface
31 */
29class AKONADI2COMMON_EXPORT Resource 32class AKONADI2COMMON_EXPORT Resource
30{ 33{
31public: 34public:
32 //TODO: configuration
33 Resource(); 35 Resource();
34 virtual ~Resource(); 36 virtual ~Resource();
35 37
@@ -44,6 +46,9 @@ private:
44 Private * const d; 46 Private * const d;
45}; 47};
46 48
49/**
50 * Factory interface for resource to implement.
51 */
47class ResourceFactory : public QObject 52class ResourceFactory : public QObject
48{ 53{
49public: 54public:
diff --git a/common/resourceaccess.cpp b/common/resourceaccess.cpp
index 7320e50..7a343f9 100644
--- a/common/resourceaccess.cpp
+++ b/common/resourceaccess.cpp
@@ -64,8 +64,8 @@ public:
64class ResourceAccess::Private 64class ResourceAccess::Private
65{ 65{
66public: 66public:
67 Private(const QString &name, ResourceAccess *ra); 67 Private(const QByteArray &name, ResourceAccess *ra);
68 QString resourceName; 68 QByteArray resourceName;
69 QLocalSocket *socket; 69 QLocalSocket *socket;
70 QTimer *tryOpenTimer; 70 QTimer *tryOpenTimer;
71 bool startingProcess; 71 bool startingProcess;
@@ -77,7 +77,7 @@ public:
77 uint messageId; 77 uint messageId;
78}; 78};
79 79
80ResourceAccess::Private::Private(const QString &name, ResourceAccess *q) 80ResourceAccess::Private::Private(const QByteArray &name, ResourceAccess *q)
81 : resourceName(name), 81 : resourceName(name),
82 socket(new QLocalSocket(q)), 82 socket(new QLocalSocket(q)),
83 tryOpenTimer(new QTimer(q)), 83 tryOpenTimer(new QTimer(q)),
@@ -87,7 +87,7 @@ ResourceAccess::Private::Private(const QString &name, ResourceAccess *q)
87{ 87{
88} 88}
89 89
90ResourceAccess::ResourceAccess(const QString &resourceName, QObject *parent) 90ResourceAccess::ResourceAccess(const QByteArray &resourceName, QObject *parent)
91 : QObject(parent), 91 : QObject(parent),
92 d(new Private(resourceName, this)) 92 d(new Private(resourceName, this))
93{ 93{
@@ -112,7 +112,7 @@ ResourceAccess::~ResourceAccess()
112 112
113} 113}
114 114
115QString ResourceAccess::resourceName() const 115QByteArray ResourceAccess::resourceName() const
116{ 116{
117 return d->resourceName; 117 return d->resourceName;
118} 118}
diff --git a/common/resourceaccess.h b/common/resourceaccess.h
index a5a2547..088bf36 100644
--- a/common/resourceaccess.h
+++ b/common/resourceaccess.h
@@ -35,10 +35,10 @@ class ResourceAccess : public QObject
35 Q_OBJECT 35 Q_OBJECT
36 36
37public: 37public:
38 ResourceAccess(const QString &resourceName, QObject *parent = 0); 38 ResourceAccess(const QByteArray &resourceName, QObject *parent = 0);
39 ~ResourceAccess(); 39 ~ResourceAccess();
40 40
41 QString resourceName() const; 41 QByteArray resourceName() const;
42 bool isReady() const; 42 bool isReady() const;
43 43
44 Async::Job<void> sendCommand(int commandId); 44 Async::Job<void> sendCommand(int commandId);
diff --git a/common/storage.h b/common/storage.h
index 78faac0..09365b0 100644
--- a/common/storage.h
+++ b/common/storage.h
@@ -98,7 +98,7 @@ public:
98 * Set the default error handler. 98 * Set the default error handler.
99 */ 99 */
100 void setDefaultErrorHandler(const std::function<void(const Storage::Error &error)> &errorHandler); 100 void setDefaultErrorHandler(const std::function<void(const Storage::Error &error)> &errorHandler);
101 std::function<void(const Storage::Error &error)> defaultErrorHandler(); 101 std::function<void(const Storage::Error &error)> defaultErrorHandler() const;
102 102
103 /** 103 /**
104 * A basic error handler that writes to std::cerr. 104 * A basic error handler that writes to std::cerr.
diff --git a/common/storage_common.cpp b/common/storage_common.cpp
index 5728096..65f6e57 100644
--- a/common/storage_common.cpp
+++ b/common/storage_common.cpp
@@ -46,7 +46,7 @@ void Storage::setDefaultErrorHandler(const std::function<void(const Storage::Err
46 mErrorHandler = errorHandler; 46 mErrorHandler = errorHandler;
47} 47}
48 48
49std::function<void(const Storage::Error &error)> Storage::defaultErrorHandler() 49std::function<void(const Storage::Error &error)> Storage::defaultErrorHandler() const
50{ 50{
51 if (mErrorHandler) { 51 if (mErrorHandler) {
52 return mErrorHandler; 52 return mErrorHandler;
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index 4fcb11f..7bbf8b5 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -425,11 +425,11 @@ qint64 Storage::diskUsage() const
425void Storage::removeFromDisk() const 425void Storage::removeFromDisk() const
426{ 426{
427 const QString fullPath(d->storageRoot + '/' + d->name); 427 const QString fullPath(d->storageRoot + '/' + d->name);
428 qDebug() << "removing " << fullPath;
429 QMutexLocker locker(&d->sMutex); 428 QMutexLocker locker(&d->sMutex);
430 QDir dir(fullPath); 429 QDir dir(fullPath);
431 if (!dir.removeRecursively()) { 430 if (!dir.removeRecursively()) {
432 qWarning() << "Failed to remove directory" << d->storageRoot << d->name; 431 Error error(d->name.toLatin1(), ErrorCodes::GenericError, QString("Failed to remove directory %1 %2").arg(d->storageRoot).arg(d->name).toLatin1());
432 defaultErrorHandler()(error);
433 } 433 }
434 auto env = d->sEnvironments.take(fullPath); 434 auto env = d->sEnvironments.take(fullPath);
435 mdb_env_close(env); 435 mdb_env_close(env);
diff --git a/common/test/clientapitest.cpp b/common/test/clientapitest.cpp
index c9e4d6d..24b3fb9 100644
--- a/common/test/clientapitest.cpp
+++ b/common/test/clientapitest.cpp
@@ -4,14 +4,14 @@
4 4
5#include "../clientapi.h" 5#include "../clientapi.h"
6 6
7class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::Domain::Event> 7class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::ApplicationDomain::Event>
8{ 8{
9public: 9public:
10 ~DummyResourceFacade(){}; 10 ~DummyResourceFacade(){};
11 virtual Async::Job<void> create(const Akonadi2::Domain::Event &domainObject){ return Async::null<void>(); }; 11 virtual Async::Job<void> create(const Akonadi2::ApplicationDomain::Event &domainObject){ return Async::null<void>(); };
12 virtual Async::Job<void> modify(const Akonadi2::Domain::Event &domainObject){ return Async::null<void>(); }; 12 virtual Async::Job<void> modify(const Akonadi2::ApplicationDomain::Event &domainObject){ return Async::null<void>(); };
13 virtual Async::Job<void> remove(const Akonadi2::Domain::Event &domainObject){ return Async::null<void>(); }; 13 virtual Async::Job<void> remove(const Akonadi2::ApplicationDomain::Event &domainObject){ return Async::null<void>(); };
14 virtual Async::Job<void> load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback) 14 virtual Async::Job<void> load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback)
15 { 15 {
16 return Async::start<void>([this, resultCallback](Async::Future<void> &future) { 16 return Async::start<void>([this, resultCallback](Async::Future<void> &future) {
17 qDebug() << "load called"; 17 qDebug() << "load called";
@@ -22,7 +22,7 @@ public:
22 }); 22 });
23 } 23 }
24 24
25 QList<Akonadi2::Domain::Event::Ptr> results; 25 QList<Akonadi2::ApplicationDomain::Event::Ptr> results;
26}; 26};
27 27
28class ClientAPITest : public QObject 28class ClientAPITest : public QObject
@@ -33,14 +33,14 @@ private Q_SLOTS:
33 void testLoad() 33 void testLoad()
34 { 34 {
35 DummyResourceFacade facade; 35 DummyResourceFacade facade;
36 facade.results << QSharedPointer<Akonadi2::Domain::Event>::create("resource", "id", 0, QSharedPointer<Akonadi2::Domain::BufferAdaptor>()); 36 facade.results << QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("resource", "id", 0, QSharedPointer<Akonadi2::ApplicationDomain::BufferAdaptor>());
37 37
38 Akonadi2::FacadeFactory::instance().registerFacade<Akonadi2::Domain::Event, DummyResourceFacade>("dummyresource", [facade](){ return new DummyResourceFacade(facade); }); 38 Akonadi2::FacadeFactory::instance().registerFacade<Akonadi2::ApplicationDomain::Event, DummyResourceFacade>("dummyresource", [facade](){ return new DummyResourceFacade(facade); });
39 39
40 Akonadi2::Query query; 40 Akonadi2::Query query;
41 query.resources << "dummyresource"; 41 query.resources << "dummyresource";
42 42
43 async::SyncListResult<Akonadi2::Domain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::Domain::Event>(query)); 43 async::SyncListResult<Akonadi2::ApplicationDomain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::ApplicationDomain::Event>(query));
44 result.exec(); 44 result.exec();
45 QCOMPARE(result.size(), 1); 45 QCOMPARE(result.size(), 1);
46 } 46 }
diff --git a/dummyresource/domainadaptor.cpp b/dummyresource/domainadaptor.cpp
index 2182f9a..fa00bbc 100644
--- a/dummyresource/domainadaptor.cpp
+++ b/dummyresource/domainadaptor.cpp
@@ -9,142 +9,55 @@
9#include "entity_generated.h" 9#include "entity_generated.h"
10#include "metadata_generated.h" 10#include "metadata_generated.h"
11#include "domainadaptor.h" 11#include "domainadaptor.h"
12#include "log.h"
12#include <common/entitybuffer.h> 13#include <common/entitybuffer.h>
13 14
14using namespace DummyCalendar; 15using namespace DummyCalendar;
15using namespace flatbuffers; 16using namespace flatbuffers;
16 17
17using namespace DummyCalendar;
18using namespace flatbuffers;
19
20//This will become a generic implementation that simply takes the resource buffer and local buffer pointer
21class DummyEventAdaptor : public Akonadi2::Domain::BufferAdaptor
22{
23public:
24 DummyEventAdaptor()
25 : BufferAdaptor()
26 {
27
28 }
29
30 void setProperty(const QString &key, const QVariant &value)
31 {
32 if (mResourceMapper && mResourceMapper->mWriteAccessors.contains(key)) {
33 // mResourceMapper->setProperty(key, value, mResourceBuffer);
34 } else {
35 // mLocalMapper.;
36 }
37 }
38 18
39 virtual QVariant getProperty(const QString &key) const
40 {
41 if (mResourceBuffer && mResourceMapper->mReadAccessors.contains(key)) {
42 return mResourceMapper->getProperty(key, mResourceBuffer);
43 } else if (mLocalBuffer && mLocalMapper->mReadAccessors.contains(key)) {
44 return mLocalMapper->getProperty(key, mLocalBuffer);
45 }
46 qWarning() << "no mapping available for key " << key;
47 return QVariant();
48 }
49
50 virtual QStringList availableProperties() const
51 {
52 QStringList props;
53 props << mResourceMapper->mReadAccessors.keys();
54 props << mLocalMapper->mReadAccessors.keys();
55 return props;
56 }
57
58 Akonadi2::Domain::Buffer::Event const *mLocalBuffer;
59 DummyEvent const *mResourceBuffer;
60
61 QSharedPointer<PropertyMapper<Akonadi2::Domain::Buffer::Event> > mLocalMapper;
62 QSharedPointer<PropertyMapper<DummyEvent> > mResourceMapper;
63};
64 19
65 20
66DummyEventAdaptorFactory::DummyEventAdaptorFactory() 21DummyEventAdaptorFactory::DummyEventAdaptorFactory()
67 : DomainTypeAdaptorFactory() 22 : DomainTypeAdaptorFactory()
68{ 23{
69 mResourceMapper = QSharedPointer<PropertyMapper<DummyEvent> >::create(); 24 //TODO turn this into initializeReadPropertyMapper as well?
70 mResourceMapper->mReadAccessors.insert("summary", [](DummyEvent const *buffer) -> QVariant { 25 mResourceMapper->addMapping("summary", [](DummyEvent const *buffer) -> QVariant {
71 if (buffer->summary()) {
72 return QString::fromStdString(buffer->summary()->c_str());
73 }
74 return QVariant();
75 });
76 mLocalMapper = QSharedPointer<PropertyMapper<Akonadi2::Domain::Buffer::Event> >::create();
77 mLocalMapper->mReadAccessors.insert("summary", [](Akonadi2::Domain::Buffer::Event const *buffer) -> QVariant {
78 if (buffer->summary()) { 26 if (buffer->summary()) {
79 return QString::fromStdString(buffer->summary()->c_str()); 27 return QString::fromStdString(buffer->summary()->c_str());
80 } 28 }
81 return QVariant(); 29 return QVariant();
82 }); 30 });
83 mLocalMapper->mReadAccessors.insert("uid", [](Akonadi2::Domain::Buffer::Event const *buffer) -> QVariant {
84 if (buffer->uid()) {
85 return QString::fromStdString(buffer->uid()->c_str());
86 }
87 return QVariant();
88 });
89 31
32 mResourceWriteMapper->addMapping("summary", [](const QVariant &value, flatbuffers::FlatBufferBuilder &fbb) -> std::function<void(DummyEventBuilder &)> {
33 auto offset = extractProperty<QString>(value, fbb);
34 return [offset](DummyEventBuilder &builder) { builder.add_summary(offset); };
35 });
90} 36}
91 37
92//TODO pass EntityBuffer instead? 38
93QSharedPointer<Akonadi2::Domain::BufferAdaptor> DummyEventAdaptorFactory::createAdaptor(const Akonadi2::Entity &entity) 39void DummyEventAdaptorFactory::createBuffer(const Akonadi2::ApplicationDomain::Event &event, flatbuffers::FlatBufferBuilder &fbb)
94{ 40{
95 DummyEvent const *resourceBuffer = 0; 41 flatbuffers::FlatBufferBuilder localFbb;
96 if (auto resourceData = entity.resource()) { 42 if (mLocalWriteMapper) {
97 flatbuffers::Verifier verifyer(resourceData->Data(), resourceData->size()); 43 auto pos = createBufferPart<Akonadi2::ApplicationDomain::Buffer::EventBuilder, Akonadi2::ApplicationDomain::Buffer::Event>(event, localFbb, *mLocalWriteMapper);
98 if (VerifyDummyEventBuffer(verifyer)) { 44 Akonadi2::ApplicationDomain::Buffer::FinishEventBuffer(localFbb, pos);
99 resourceBuffer = GetDummyEvent(resourceData->Data()); 45 flatbuffers::Verifier verifier(localFbb.GetBufferPointer(), localFbb.GetSize());
46 if (!verifier.VerifyBuffer<Akonadi2::ApplicationDomain::Buffer::Event>()) {
47 Warning() << "Created invalid local buffer";
100 } 48 }
101 } 49 }
102 50
103 // Akonadi2::Metadata const *metadataBuffer = 0; 51 flatbuffers::FlatBufferBuilder resFbb;
104 // if (auto metadataData = entity.metadata()) { 52 if (mResourceWriteMapper) {
105 // flatbuffers::Verifier verifyer(metadataData->Data(), metadataData->size()); 53 auto pos = createBufferPart<DummyEventBuilder, DummyEvent>(event, resFbb, *mResourceWriteMapper);
106 // if (Akonadi2::VerifyMetadataBuffer(verifyer)) { 54 DummyCalendar::FinishDummyEventBuffer(resFbb, pos);
107 // metadataBuffer = Akonadi2::GetMetadata(metadataData->Data()); 55 flatbuffers::Verifier verifier(resFbb.GetBufferPointer(), resFbb.GetSize());
108 // } 56 if (!verifier.VerifyBuffer<DummyEvent>()) {
109 // } 57 Warning() << "Created invalid resource buffer";
110
111 Akonadi2::Domain::Buffer::Event const *localBuffer = 0;
112 if (auto localData = entity.local()) {
113 flatbuffers::Verifier verifyer(localData->Data(), localData->size());
114 if (Akonadi2::Domain::Buffer::VerifyEventBuffer(verifyer)) {
115 localBuffer = Akonadi2::Domain::Buffer::GetEvent(localData->Data());
116 } 58 }
117 } 59 }
118 60
119 auto adaptor = QSharedPointer<DummyEventAdaptor>::create(); 61 Akonadi2::EntityBuffer::assembleEntityBuffer(fbb, 0, 0, resFbb.GetBufferPointer(), resFbb.GetSize(), localFbb.GetBufferPointer(), localFbb.GetSize());
120 adaptor->mLocalBuffer = localBuffer;
121 adaptor->mLocalMapper = mLocalMapper;
122 adaptor->mResourceBuffer = resourceBuffer;
123 adaptor->mResourceMapper = mResourceMapper;
124 return adaptor;
125}
126
127void DummyEventAdaptorFactory::createBuffer(const Akonadi2::Domain::Event &event, flatbuffers::FlatBufferBuilder &fbb)
128{
129 flatbuffers::FlatBufferBuilder eventFbb;
130 eventFbb.Clear();
131 {
132 auto summary = eventFbb.CreateString(event.getProperty("summary").toString().toStdString());
133 DummyCalendar::DummyEventBuilder eventBuilder(eventFbb);
134 eventBuilder.add_summary(summary);
135 auto eventLocation = eventBuilder.Finish();
136 DummyCalendar::FinishDummyEventBuffer(eventFbb, eventLocation);
137 }
138
139 flatbuffers::FlatBufferBuilder localFbb;
140 {
141 auto uid = localFbb.CreateString(event.getProperty("uid").toString().toStdString());
142 auto localBuilder = Akonadi2::Domain::Buffer::EventBuilder(localFbb);
143 localBuilder.add_uid(uid);
144 auto location = localBuilder.Finish();
145 Akonadi2::Domain::Buffer::FinishEventBuffer(localFbb, location);
146 }
147
148 Akonadi2::EntityBuffer::assembleEntityBuffer(fbb, 0, 0, eventFbb.GetBufferPointer(), eventFbb.GetSize(), localFbb.GetBufferPointer(), localFbb.GetSize());
149} 62}
150 63
diff --git a/dummyresource/domainadaptor.h b/dummyresource/domainadaptor.h
index 9474176..9d351e7 100644
--- a/dummyresource/domainadaptor.h
+++ b/dummyresource/domainadaptor.h
@@ -1,4 +1,3 @@
1
2#pragma once 1#pragma once
3 2
4#include "common/domainadaptor.h" 3#include "common/domainadaptor.h"
@@ -6,11 +5,10 @@
6#include "dummycalendar_generated.h" 5#include "dummycalendar_generated.h"
7#include "entity_generated.h" 6#include "entity_generated.h"
8 7
9class DummyEventAdaptorFactory : public DomainTypeAdaptorFactory<Akonadi2::Domain::Event, Akonadi2::Domain::Buffer::Event, DummyCalendar::DummyEvent> 8class DummyEventAdaptorFactory : public DomainTypeAdaptorFactory<Akonadi2::ApplicationDomain::Event, Akonadi2::ApplicationDomain::Buffer::Event, DummyCalendar::DummyEvent, Akonadi2::ApplicationDomain::Buffer::EventBuilder, DummyCalendar::DummyEventBuilder>
10{ 9{
11public: 10public:
12 DummyEventAdaptorFactory(); 11 DummyEventAdaptorFactory();
13 virtual ~DummyEventAdaptorFactory() {}; 12 virtual ~DummyEventAdaptorFactory() {};
14 virtual QSharedPointer<Akonadi2::Domain::BufferAdaptor> createAdaptor(const Akonadi2::Entity &entity); 13 virtual void createBuffer(const Akonadi2::ApplicationDomain::Event &event, flatbuffers::FlatBufferBuilder &fbb);
15 virtual void createBuffer(const Akonadi2::Domain::Event &event, flatbuffers::FlatBufferBuilder &fbb);
16}; 14};
diff --git a/dummyresource/facade.cpp b/dummyresource/facade.cpp
index 949b4c4..1477fcf 100644
--- a/dummyresource/facade.cpp
+++ b/dummyresource/facade.cpp
@@ -28,7 +28,6 @@
28#include "event_generated.h" 28#include "event_generated.h"
29#include "entity_generated.h" 29#include "entity_generated.h"
30#include "metadata_generated.h" 30#include "metadata_generated.h"
31#include "createentity_generated.h"
32#include "domainadaptor.h" 31#include "domainadaptor.h"
33#include <common/entitybuffer.h> 32#include <common/entitybuffer.h>
34#include <common/index.h> 33#include <common/index.h>
@@ -37,9 +36,9 @@
37using namespace DummyCalendar; 36using namespace DummyCalendar;
38using namespace flatbuffers; 37using namespace flatbuffers;
39 38
39
40DummyResourceFacade::DummyResourceFacade() 40DummyResourceFacade::DummyResourceFacade()
41 : Akonadi2::StoreFacade<Akonadi2::Domain::Event>(), 41 : Akonadi2::GenericFacade<Akonadi2::ApplicationDomain::Event>("org.kde.dummy"),
42 mResourceAccess(new Akonadi2::ResourceAccess("org.kde.dummy")),
43 mFactory(new DummyEventAdaptorFactory) 42 mFactory(new DummyEventAdaptorFactory)
44{ 43{
45} 44}
@@ -48,38 +47,30 @@ DummyResourceFacade::~DummyResourceFacade()
48{ 47{
49} 48}
50 49
51Async::Job<void> DummyResourceFacade::create(const Akonadi2::Domain::Event &domainObject) 50Async::Job<void> DummyResourceFacade::create(const Akonadi2::ApplicationDomain::Event &domainObject)
52{ 51{
53 flatbuffers::FlatBufferBuilder entityFbb; 52 flatbuffers::FlatBufferBuilder entityFbb;
54 mFactory->createBuffer(domainObject, entityFbb); 53 mFactory->createBuffer(domainObject, entityFbb);
55 54 return sendCreateCommand("event", QByteArray::fromRawData(reinterpret_cast<const char*>(entityFbb.GetBufferPointer()), entityFbb.GetSize()));
56 flatbuffers::FlatBufferBuilder fbb;
57 //This is the resource buffer type and not the domain type
58 auto type = fbb.CreateString("event");
59 auto delta = fbb.CreateVector<uint8_t>(entityFbb.GetBufferPointer(), entityFbb.GetSize());
60 auto location = Akonadi2::Commands::CreateCreateEntity(fbb, type, delta);
61 Akonadi2::Commands::FinishCreateEntityBuffer(fbb, location);
62 mResourceAccess->open();
63 return mResourceAccess->sendCommand(Akonadi2::Commands::CreateEntityCommand, fbb);
64} 55}
65 56
66Async::Job<void> DummyResourceFacade::modify(const Akonadi2::Domain::Event &domainObject) 57Async::Job<void> DummyResourceFacade::modify(const Akonadi2::ApplicationDomain::Event &domainObject)
67{ 58{
68 //Create message buffer and send to resource 59 //Create message buffer and send to resource
69 return Async::null<void>(); 60 return Async::null<void>();
70} 61}
71 62
72Async::Job<void> DummyResourceFacade::remove(const Akonadi2::Domain::Event &domainObject) 63Async::Job<void> DummyResourceFacade::remove(const Akonadi2::ApplicationDomain::Event &domainObject)
73{ 64{
74 //Create message buffer and send to resource 65 //Create message buffer and send to resource
75 return Async::null<void>(); 66 return Async::null<void>();
76} 67}
77 68
78static std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local)> prepareQuery(const Akonadi2::Query &query) 69static std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local)> prepareQuery(const Akonadi2::Query &query)
79{ 70{
80 //Compose some functions to make query matching fast. 71 //Compose some functions to make query matching fast.
81 //This way we can process the query once, and convert all values into something that can be compared quickly 72 //This way we can process the query once, and convert all values into something that can be compared quickly
82 std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local)> preparedQuery; 73 std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local)> preparedQuery;
83 if (!query.ids.isEmpty()) { 74 if (!query.ids.isEmpty()) {
84 //Match by id 75 //Match by id
85 //TODO: for id's a direct lookup would be way faster 76 //TODO: for id's a direct lookup would be way faster
@@ -90,7 +81,7 @@ static std::function<bool(const std::string &key, DummyEvent const *buffer, Akon
90 for (const auto &id : query.ids) { 81 for (const auto &id : query.ids) {
91 ids << id.toStdString(); 82 ids << id.toStdString();
92 } 83 }
93 preparedQuery = [ids](const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local) { 84 preparedQuery = [ids](const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local) {
94 if (ids.contains(key)) { 85 if (ids.contains(key)) {
95 return true; 86 return true;
96 } 87 }
@@ -99,7 +90,7 @@ static std::function<bool(const std::string &key, DummyEvent const *buffer, Akon
99 } else if (!query.propertyFilter.isEmpty()) { 90 } else if (!query.propertyFilter.isEmpty()) {
100 if (query.propertyFilter.contains("uid")) { 91 if (query.propertyFilter.contains("uid")) {
101 const QByteArray uid = query.propertyFilter.value("uid").toByteArray(); 92 const QByteArray uid = query.propertyFilter.value("uid").toByteArray();
102 preparedQuery = [uid](const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local) { 93 preparedQuery = [uid](const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local) {
103 if (local && local->uid() && (QByteArray::fromRawData(local->uid()->c_str(), local->uid()->size()) == uid)) { 94 if (local && local->uid() && (QByteArray::fromRawData(local->uid()->c_str(), local->uid()->size()) == uid)) {
104 return true; 95 return true;
105 } 96 }
@@ -108,32 +99,14 @@ static std::function<bool(const std::string &key, DummyEvent const *buffer, Akon
108 } 99 }
109 } else { 100 } else {
110 //Match everything 101 //Match everything
111 preparedQuery = [](const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local) { 102 preparedQuery = [](const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local) {
112 return true; 103 return true;
113 }; 104 };
114 } 105 }
115 return preparedQuery; 106 return preparedQuery;
116} 107}
117 108
118Async::Job<void> DummyResourceFacade::synchronizeResource(bool sync, bool processAll) 109void DummyResourceFacade::readValue(QSharedPointer<Akonadi2::Storage> storage, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback, std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local)> preparedQuery)
119{
120 //TODO check if a sync is necessary
121 //TODO Only sync what was requested
122 //TODO timeout
123
124 if (sync || processAll) {
125 return Async::start<void>([=](Async::Future<void> &future) {
126 mResourceAccess->open();
127 mResourceAccess->synchronizeResource(sync, processAll).then<void>([&future](Async::Future<void> &f) {
128 future.setFinished();
129 f.setFinished();
130 }).exec();
131 });
132 }
133 return Async::null<void>();
134}
135
136void DummyResourceFacade::readValue(QSharedPointer<Akonadi2::Storage> storage, const QByteArray &key, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local)> preparedQuery)
137{ 110{
138 storage->scan(key, [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { 111 storage->scan(key, [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool {
139 112
@@ -145,31 +118,11 @@ void DummyResourceFacade::readValue(QSharedPointer<Akonadi2::Storage> storage, c
145 //Extract buffers 118 //Extract buffers
146 Akonadi2::EntityBuffer buffer(dataValue, dataSize); 119 Akonadi2::EntityBuffer buffer(dataValue, dataSize);
147 120
148 DummyEvent const *resourceBuffer = 0; 121 const auto resourceBuffer = Akonadi2::EntityBuffer::readBuffer<DummyEvent>(buffer.entity().resource());
149 if (auto resourceData = buffer.entity().resource()) { 122 const auto localBuffer = Akonadi2::EntityBuffer::readBuffer<Akonadi2::ApplicationDomain::Buffer::Event>(buffer.entity().local());
150 flatbuffers::Verifier verifyer(resourceData->Data(), resourceData->size()); 123 const auto metadataBuffer = Akonadi2::EntityBuffer::readBuffer<Akonadi2::Metadata>(buffer.entity().metadata());
151 if (VerifyDummyEventBuffer(verifyer)) {
152 resourceBuffer = GetDummyEvent(resourceData->Data());
153 }
154 }
155
156 Akonadi2::Domain::Buffer::Event const *localBuffer = 0;
157 if (auto localData = buffer.entity().local()) {
158 flatbuffers::Verifier verifyer(localData->Data(), localData->size());
159 if (Akonadi2::Domain::Buffer::VerifyEventBuffer(verifyer)) {
160 localBuffer = Akonadi2::Domain::Buffer::GetEvent(localData->Data());
161 }
162 }
163
164 Akonadi2::Metadata const *metadataBuffer = 0;
165 if (auto metadataData = buffer.entity().metadata()) {
166 flatbuffers::Verifier verifyer(metadataData->Data(), metadataData->size());
167 if (Akonadi2::VerifyMetadataBuffer(verifyer)) {
168 metadataBuffer = Akonadi2::GetMetadata(metadataData->Data());
169 }
170 }
171 124
172 if (!resourceBuffer || !metadataBuffer) { 125 if ((!resourceBuffer && !localBuffer) || !metadataBuffer) {
173 qWarning() << "invalid buffer " << QByteArray::fromRawData(static_cast<char*>(keyValue), keySize); 126 qWarning() << "invalid buffer " << QByteArray::fromRawData(static_cast<char*>(keyValue), keySize);
174 return true; 127 return true;
175 } 128 }
@@ -182,8 +135,9 @@ void DummyResourceFacade::readValue(QSharedPointer<Akonadi2::Storage> storage, c
182 //Not i.e. for tags that are stored as flags in each entity of an imap store. 135 //Not i.e. for tags that are stored as flags in each entity of an imap store.
183 auto adaptor = mFactory->createAdaptor(buffer.entity()); 136 auto adaptor = mFactory->createAdaptor(buffer.entity());
184 //TODO only copy requested properties 137 //TODO only copy requested properties
185 auto memoryAdaptor = QSharedPointer<Akonadi2::Domain::MemoryBufferAdaptor>::create(*adaptor); 138 auto memoryAdaptor = QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create(*adaptor);
186 auto event = QSharedPointer<Akonadi2::Domain::Event>::create("org.kde.dummy", QString::fromUtf8(static_cast<char*>(keyValue), keySize), revision, memoryAdaptor); 139 // here we could copy additional properties that don't have a 1:1 mapping, such as separately stored tags.
140 auto event = QSharedPointer<Akonadi2::ApplicationDomain::Event>::create("org.kde.dummy", QByteArray::fromRawData(static_cast<char*>(keyValue), keySize), revision, memoryAdaptor);
187 resultCallback(event); 141 resultCallback(event);
188 } 142 }
189 return true; 143 return true;
@@ -193,7 +147,7 @@ void DummyResourceFacade::readValue(QSharedPointer<Akonadi2::Storage> storage, c
193 }); 147 });
194} 148}
195 149
196Async::Job<void> DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback) 150Async::Job<void> DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback)
197{ 151{
198 return synchronizeResource(query.syncOnDemand, query.processAll).then<void>([=](Async::Future<void> &future) { 152 return synchronizeResource(query.syncOnDemand, query.processAll).then<void>([=](Async::Future<void> &future) {
199 //Now that the sync is complete we can execute the query 153 //Now that the sync is complete we can execute the query
@@ -201,6 +155,9 @@ Async::Job<void> DummyResourceFacade::load(const Akonadi2::Query &query, const s
201 155
202 auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy"); 156 auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy");
203 157
158 //TODO use transaction over full query and record store revision. We'll need it to update the query.
159
160 //Index lookups
204 QVector<QByteArray> keys; 161 QVector<QByteArray> keys;
205 if (query.propertyFilter.contains("uid")) { 162 if (query.propertyFilter.contains("uid")) {
206 static Index uidIndex(Akonadi2::Store::storageLocation(), "org.kde.dummy.index.uid", Akonadi2::Storage::ReadOnly); 163 static Index uidIndex(Akonadi2::Store::storageLocation(), "org.kde.dummy.index.uid", Akonadi2::Storage::ReadOnly);
diff --git a/dummyresource/facade.h b/dummyresource/facade.h
index 1f69161..37ed81d 100644
--- a/dummyresource/facade.h
+++ b/dummyresource/facade.h
@@ -19,6 +19,8 @@
19 19
20#pragma once 20#pragma once
21 21
22#include "common/facade.h"
23
22#include "common/clientapi.h" 24#include "common/clientapi.h"
23#include "common/storage.h" 25#include "common/storage.h"
24#include "resourcefactory.h" 26#include "resourcefactory.h"
@@ -27,24 +29,17 @@
27#include "dummycalendar_generated.h" 29#include "dummycalendar_generated.h"
28#include "common/domainadaptor.h" 30#include "common/domainadaptor.h"
29 31
30namespace Akonadi2 { 32class DummyResourceFacade : public Akonadi2::GenericFacade<Akonadi2::ApplicationDomain::Event>
31 class ResourceAccess;
32}
33
34
35class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::Domain::Event>
36{ 33{
37public: 34public:
38 DummyResourceFacade(); 35 DummyResourceFacade();
39 virtual ~DummyResourceFacade(); 36 virtual ~DummyResourceFacade();
40 virtual Async::Job<void> create(const Akonadi2::Domain::Event &domainObject); 37 virtual Async::Job<void> create(const Akonadi2::ApplicationDomain::Event &domainObject);
41 virtual Async::Job<void> modify(const Akonadi2::Domain::Event &domainObject); 38 virtual Async::Job<void> modify(const Akonadi2::ApplicationDomain::Event &domainObject);
42 virtual Async::Job<void> remove(const Akonadi2::Domain::Event &domainObject); 39 virtual Async::Job<void> remove(const Akonadi2::ApplicationDomain::Event &domainObject);
43 virtual Async::Job<void> load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback); 40 virtual Async::Job<void> load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback);
44 41
45private: 42private:
46 void readValue(QSharedPointer<Akonadi2::Storage> storage, const QByteArray &key, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, std::function<bool(const std::string &key, DummyCalendar::DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local)>); 43 void readValue(QSharedPointer<Akonadi2::Storage> storage, const QByteArray &key, const std::function<void(const Akonadi2::ApplicationDomain::Event::Ptr &)> &resultCallback, std::function<bool(const std::string &key, DummyCalendar::DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local)>);
47 Async::Job<void> synchronizeResource(bool sync, bool processAll); 44 QSharedPointer<DomainTypeAdaptorFactory<Akonadi2::ApplicationDomain::Event, Akonadi2::ApplicationDomain::Buffer::Event, DummyCalendar::DummyEvent, Akonadi2::ApplicationDomain::Buffer::EventBuilder, DummyCalendar::DummyEventBuilder> > mFactory;
48 QSharedPointer<Akonadi2::ResourceAccess> mResourceAccess;
49 QSharedPointer<DomainTypeAdaptorFactory<Akonadi2::Domain::Event, Akonadi2::Domain::Buffer::Event, DummyCalendar::DummyEvent> > mFactory;
50}; 45};
diff --git a/dummyresource/resourcefactory.cpp b/dummyresource/resourcefactory.cpp
index dda34ef..8f0a2bd 100644
--- a/dummyresource/resourcefactory.cpp
+++ b/dummyresource/resourcefactory.cpp
@@ -191,7 +191,7 @@ private slots:
191 return; 191 return;
192 } 192 }
193 auto queuedCommand = Akonadi2::GetQueuedCommand(ptr); 193 auto queuedCommand = Akonadi2::GetQueuedCommand(ptr);
194 qDebug() << "Dequeued: " << queuedCommand->commandId(); 194 Trace() << "Dequeued: " << queuedCommand->commandId();
195 //TODO JOBAPI: job lifetime management 195 //TODO JOBAPI: job lifetime management
196 //Right now we're just leaking jobs. In this case we'd like jobs that are heap allocated and delete 196 //Right now we're just leaking jobs. In this case we'd like jobs that are heap allocated and delete
197 //themselves once done. In other cases we'd like jobs that only live as long as their handle though. 197 //themselves once done. In other cases we'd like jobs that only live as long as their handle though.
@@ -256,29 +256,18 @@ void DummyResource::configurePipeline(Akonadi2::Pipeline *pipeline)
256 //Eventually the order should be self configuring, for now it's hardcoded. 256 //Eventually the order should be self configuring, for now it's hardcoded.
257 auto eventIndexer = new SimpleProcessor("summaryprocessor", [eventFactory](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity) { 257 auto eventIndexer = new SimpleProcessor("summaryprocessor", [eventFactory](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity) {
258 auto adaptor = eventFactory->createAdaptor(entity); 258 auto adaptor = eventFactory->createAdaptor(entity);
259 // qDebug() << "Summary preprocessor: " << adaptor->getProperty("summary").toString(); 259 // Log() << "Summary preprocessor: " << adaptor->getProperty("summary").toString();
260 }); 260 });
261 261
262 auto uidIndexer = new SimpleProcessor("uidIndexer", [eventFactory](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity) { 262 auto uidIndexer = new SimpleProcessor("uidIndexer", [eventFactory](const Akonadi2::PipelineState &state, const Akonadi2::Entity &entity) {
263 static Index uidIndex(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde.dummy.index.uid", Akonadi2::Storage::ReadWrite); 263 static Index uidIndex(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/akonadi2/storage", "org.kde.dummy.index.uid", Akonadi2::Storage::ReadWrite);
264 264
265 //TODO: Benchmark if this is performance wise acceptable, or if we have to access the buffer directly
265 auto adaptor = eventFactory->createAdaptor(entity); 266 auto adaptor = eventFactory->createAdaptor(entity);
266 const auto uid = adaptor->getProperty("uid"); 267 const auto uid = adaptor->getProperty("uid");
267 if (uid.isValid()) { 268 if (uid.isValid()) {
268 uidIndex.add(uid.toByteArray(), state.key()); 269 uidIndex.add(uid.toByteArray(), state.key());
269 } 270 }
270
271 //TODO would this be worthwhile for performance reasons?
272 // flatbuffers::Verifier verifyer(entity.local()->Data(), entity.local()->size());
273 // if (!Akonadi2::Domain::Buffer::VerifyEventBuffer(verifyer)) {
274 // qWarning() << "invalid local buffer";
275 // return;
276 // }
277 // auto localEvent = Akonadi2::Domain::Buffer::GetEvent(entity.local()->Data());
278 // if (localEvent && localEvent->uid()) {
279 // qDebug() << "got uid: " << QByteArray::fromRawData(reinterpret_cast<const char *>(localEvent->uid()->Data()), localEvent->uid()->size());
280 // uidIndex.add(QByteArray::fromRawData(reinterpret_cast<const char *>(localEvent->uid()->Data()), localEvent->uid()->size()), state.key());
281 // }
282 }); 271 });
283 272
284 //event is the entitytype and not the domain type 273 //event is the entitytype and not the domain type
@@ -289,7 +278,7 @@ void DummyResource::configurePipeline(Akonadi2::Pipeline *pipeline)
289 278
290void DummyResource::onProcessorError(int errorCode, const QString &errorMessage) 279void DummyResource::onProcessorError(int errorCode, const QString &errorMessage)
291{ 280{
292 qWarning() << "Received error from Processor: " << errorCode << errorMessage; 281 Warning() << "Received error from Processor: " << errorCode << errorMessage;
293 mError = errorCode; 282 mError = errorCode;
294} 283}
295 284
@@ -325,11 +314,8 @@ void findByRemoteId(QSharedPointer<Akonadi2::Storage> storage, const QString &ri
325void DummyResource::enqueueCommand(MessageQueue &mq, int commandId, const QByteArray &data) 314void DummyResource::enqueueCommand(MessageQueue &mq, int commandId, const QByteArray &data)
326{ 315{
327 m_fbb.Clear(); 316 m_fbb.Clear();
328 auto commandData = m_fbb.CreateVector(reinterpret_cast<uint8_t const *>(data.data()), data.size()); 317 auto commandData = Akonadi2::EntityBuffer::appendAsVector(m_fbb, data.constData(), data.size());
329 auto builder = Akonadi2::QueuedCommandBuilder(m_fbb); 318 auto buffer = Akonadi2::CreateQueuedCommand(m_fbb, commandId, commandData);
330 builder.add_commandId(commandId);
331 builder.add_command(commandData);
332 auto buffer = builder.Finish();
333 Akonadi2::FinishQueuedCommandBuffer(m_fbb, buffer); 319 Akonadi2::FinishQueuedCommandBuffer(m_fbb, buffer);
334 mq.enqueue(m_fbb.GetBufferPointer(), m_fbb.GetSize()); 320 mq.enqueue(m_fbb.GetBufferPointer(), m_fbb.GetSize());
335} 321}
@@ -357,7 +343,7 @@ Async::Job<void> DummyResource::synchronizeWithSource(Akonadi2::Pipeline *pipeli
357 auto rid = m_fbb.CreateString(it.key().toStdString().c_str()); 343 auto rid = m_fbb.CreateString(it.key().toStdString().c_str());
358 auto description = m_fbb.CreateString(it.key().toStdString().c_str()); 344 auto description = m_fbb.CreateString(it.key().toStdString().c_str());
359 static uint8_t rawData[100]; 345 static uint8_t rawData[100];
360 auto attachment = m_fbb.CreateVector(rawData, 100); 346 auto attachment = Akonadi2::EntityBuffer::appendAsVector(m_fbb, rawData, 100);
361 347
362 auto builder = DummyCalendar::DummyEventBuilder(m_fbb); 348 auto builder = DummyCalendar::DummyEventBuilder(m_fbb);
363 builder.add_summary(summary); 349 builder.add_summary(summary);
@@ -372,7 +358,7 @@ Async::Job<void> DummyResource::synchronizeWithSource(Akonadi2::Pipeline *pipeli
372 flatbuffers::FlatBufferBuilder fbb; 358 flatbuffers::FlatBufferBuilder fbb;
373 //This is the resource type and not the domain type 359 //This is the resource type and not the domain type
374 auto type = fbb.CreateString("event"); 360 auto type = fbb.CreateString("event");
375 auto delta = fbb.CreateVector<uint8_t>(entityFbb.GetBufferPointer(), entityFbb.GetSize()); 361 auto delta = Akonadi2::EntityBuffer::appendAsVector(fbb, entityFbb.GetBufferPointer(), entityFbb.GetSize());
376 auto location = Akonadi2::Commands::CreateCreateEntity(fbb, type, delta); 362 auto location = Akonadi2::Commands::CreateCreateEntity(fbb, type, delta);
377 Akonadi2::Commands::FinishCreateEntityBuffer(fbb, location); 363 Akonadi2::Commands::FinishCreateEntityBuffer(fbb, location);
378 364
@@ -388,16 +374,22 @@ Async::Job<void> DummyResource::synchronizeWithSource(Akonadi2::Pipeline *pipeli
388 374
389Async::Job<void> DummyResource::processAllMessages() 375Async::Job<void> DummyResource::processAllMessages()
390{ 376{
377 //We have to wait for all items to be processed to ensure the synced items are available when a query gets executed.
378 //TODO: report errors while processing sync?
379 //TODO JOBAPI: A helper that waits for n events and then continues?
391 return Async::start<void>([this](Async::Future<void> &f) { 380 return Async::start<void>([this](Async::Future<void> &f) {
392 //We have to wait for all items to be processed to ensure the synced items are available when a query gets executed.
393 //TODO: report errors while processing sync?
394 //TODO: also check user-queue?
395 if (mSynchronizerQueue.isEmpty()) { 381 if (mSynchronizerQueue.isEmpty()) {
396 qDebug() << "synchronizer queue is empty";
397 f.setFinished(); 382 f.setFinished();
398 } else { 383 } else {
399 QObject::connect(&mSynchronizerQueue, &MessageQueue::drained, [&f]() { 384 QObject::connect(&mSynchronizerQueue, &MessageQueue::drained, [&f]() {
400 qDebug() << "synchronizer queue drained"; 385 f.setFinished();
386 });
387 }
388 }).then<void>([this](Async::Future<void> &f) {
389 if (mUserQueue.isEmpty()) {
390 f.setFinished();
391 } else {
392 QObject::connect(&mUserQueue, &MessageQueue::drained, [&f]() {
401 f.setFinished(); 393 f.setFinished();
402 }); 394 });
403 } 395 }
@@ -425,7 +417,7 @@ Akonadi2::Resource *DummyResourceFactory::createResource()
425 417
426void DummyResourceFactory::registerFacades(Akonadi2::FacadeFactory &factory) 418void DummyResourceFactory::registerFacades(Akonadi2::FacadeFactory &factory)
427{ 419{
428 factory.registerFacade<Akonadi2::Domain::Event, DummyResourceFacade>(PLUGIN_NAME); 420 factory.registerFacade<Akonadi2::ApplicationDomain::Event, DummyResourceFacade>(PLUGIN_NAME);
429} 421}
430 422
431#include "resourcefactory.moc" 423#include "resourcefactory.moc"
diff --git a/synchronizer/listener.cpp b/synchronizer/listener.cpp
index 2e1e918..7481537 100644
--- a/synchronizer/listener.cpp
+++ b/synchronizer/listener.cpp
@@ -35,7 +35,7 @@
35#include <QLocalSocket> 35#include <QLocalSocket>
36#include <QTimer> 36#include <QTimer>
37 37
38Listener::Listener(const QString &resourceName, QObject *parent) 38Listener::Listener(const QByteArray &resourceName, QObject *parent)
39 : QObject(parent), 39 : QObject(parent),
40 m_server(new QLocalServer(this)), 40 m_server(new QLocalServer(this)),
41 m_resourceName(resourceName), 41 m_resourceName(resourceName),
@@ -48,11 +48,11 @@ Listener::Listener(const QString &resourceName, QObject *parent)
48 this, &Listener::refreshRevision); 48 this, &Listener::refreshRevision);
49 connect(m_server, &QLocalServer::newConnection, 49 connect(m_server, &QLocalServer::newConnection,
50 this, &Listener::acceptConnection); 50 this, &Listener::acceptConnection);
51 Log() << QString("Trying to open %1").arg(resourceName); 51 Log() << "Trying to open " << m_resourceName;
52 if (!m_server->listen(resourceName)) { 52 if (!m_server->listen(QString::fromLatin1(resourceName))) {
53 // FIXME: multiple starts need to be handled here 53 // FIXME: multiple starts need to be handled here
54 m_server->removeServer(resourceName); 54 m_server->removeServer(resourceName);
55 if (!m_server->listen(resourceName)) { 55 if (!m_server->listen(QString::fromLatin1(resourceName))) {
56 Warning() << "Utter failure to start server"; 56 Warning() << "Utter failure to start server";
57 exit(-1); 57 exit(-1);
58 } 58 }
@@ -212,26 +212,16 @@ void Listener::processCommand(int commandId, uint messageId, Client &client, uin
212 Warning() << "No resource loaded"; 212 Warning() << "No resource loaded";
213 break; 213 break;
214 } 214 }
215 //TODO a more elegant composition of jobs should be possible 215 auto job = Async::null<void>();
216 if (buffer->sourceSync()) { 216 if (buffer->sourceSync()) {
217 bool localSync = buffer->localSync(); 217 job = m_resource->synchronizeWithSource(m_pipeline);
218 m_resource->synchronizeWithSource(m_pipeline).then<void>([callback, localSync, this](Async::Future<void> &f){
219 if (localSync) {
220 m_resource->processAllMessages().then<void>([callback](Async::Future<void> &f){
221 callback();
222 f.setFinished();
223 }).exec();
224 } else {
225 callback();
226 f.setFinished();
227 }
228 }).exec();
229 } else if (buffer->localSync()) {
230 m_resource->processAllMessages().then<void>([callback](Async::Future<void> &f){
231 callback();
232 f.setFinished();
233 }).exec();
234 } 218 }
219 if (buffer->localSync()) {
220 job = job.then<void>(m_resource->processAllMessages());
221 }
222 job.then<void>([callback]() {
223 callback();
224 }).exec();
235 return; 225 return;
236 } else { 226 } else {
237 Warning() << "received invalid command"; 227 Warning() << "received invalid command";
@@ -375,10 +365,10 @@ void Listener::loadResource()
375 Log() << QString("Resource factory: %1").arg((qlonglong)resourceFactory); 365 Log() << QString("Resource factory: %1").arg((qlonglong)resourceFactory);
376 Log() << QString("\tResource: %1").arg((qlonglong)m_resource); 366 Log() << QString("\tResource: %1").arg((qlonglong)m_resource);
377 //TODO: this doesn't really list all the facades .. fix 367 //TODO: this doesn't really list all the facades .. fix
378 Log() << QString("\tFacades: %1").arg(Akonadi2::FacadeFactory::instance().getFacade<Akonadi2::Domain::Event>(m_resourceName)->type()); 368 Log() << "\tFacades: " << Akonadi2::FacadeFactory::instance().getFacade<Akonadi2::ApplicationDomain::Event>(m_resourceName)->type();
379 m_resource->configurePipeline(m_pipeline); 369 m_resource->configurePipeline(m_pipeline);
380 } else { 370 } else {
381 Error() << QString("Failed to load resource %1").arg(m_resourceName); 371 Error() << "Failed to load resource " << m_resourceName;
382 } 372 }
383 //TODO: on failure ... what? 373 //TODO: on failure ... what?
384 //Enter broken state? 374 //Enter broken state?
diff --git a/synchronizer/listener.h b/synchronizer/listener.h
index 8dad3a4..d2ef0fc 100644
--- a/synchronizer/listener.h
+++ b/synchronizer/listener.h
@@ -58,7 +58,7 @@ class Listener : public QObject
58 Q_OBJECT 58 Q_OBJECT
59 59
60public: 60public:
61 Listener(const QString &resourceName, QObject *parent = 0); 61 Listener(const QByteArray &resourceName, QObject *parent = 0);
62 ~Listener(); 62 ~Listener();
63 63
64Q_SIGNALS: 64Q_SIGNALS:
@@ -87,7 +87,7 @@ private:
87 QLocalServer *m_server; 87 QLocalServer *m_server;
88 QVector<Client> m_connections; 88 QVector<Client> m_connections;
89 flatbuffers::FlatBufferBuilder m_fbb; 89 flatbuffers::FlatBufferBuilder m_fbb;
90 const QString m_resourceName; 90 const QByteArray m_resourceName;
91 Akonadi2::Resource *m_resource; 91 Akonadi2::Resource *m_resource;
92 Akonadi2::Pipeline *m_pipeline; 92 Akonadi2::Pipeline *m_pipeline;
93 QTimer *m_clientBufferProcessesTimer; 93 QTimer *m_clientBufferProcessesTimer;
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 8257c8a..d3e6870 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -2,6 +2,7 @@ add_subdirectory(hawd)
2 2
3set(CMAKE_AUTOMOC ON) 3set(CMAKE_AUTOMOC ON)
4include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/hawd) 4include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/hawd)
5include_directories (${CMAKE_CURRENT_BINARY_DIR}/../dummyresource)
5 6
6generate_flatbuffers(calendar) 7generate_flatbuffers(calendar)
7 8
@@ -13,14 +14,26 @@ macro(manual_tests)
13 endforeach(_testname) 14 endforeach(_testname)
14endmacro(manual_tests) 15endmacro(manual_tests)
15 16
17macro(auto_tests)
18 foreach(_testname ${ARGN})
19 add_executable(${_testname} ${_testname}.cpp)
20 add_test(${_testname} ${_testname})
21 qt5_use_modules(${_testname} Core Test Concurrent)
22 target_link_libraries(${_testname} akonadi2common libhawd)
23 endforeach(_testname)
24endmacro(auto_tests)
25
16manual_tests ( 26manual_tests (
17 storagebenchmark 27 storagebenchmark
28 dummyresourcebenchmark
29)
30
31auto_tests (
18 storagetest 32 storagetest
19 dummyresourcetest 33 dummyresourcetest
20 domainadaptortest 34 domainadaptortest
21 messagequeuetest 35 messagequeuetest
22 indextest 36 indextest
23 dummyresourcebenchmark
24) 37)
25 38
26target_link_libraries(dummyresourcetest akonadi2_resource_dummy) 39target_link_libraries(dummyresourcetest akonadi2_resource_dummy)
diff --git a/tests/domainadaptortest.cpp b/tests/domainadaptortest.cpp
index cedbf94..1e285dc 100644
--- a/tests/domainadaptortest.cpp
+++ b/tests/domainadaptortest.cpp
@@ -13,89 +13,12 @@
13#include "metadata_generated.h" 13#include "metadata_generated.h"
14#include "entity_generated.h" 14#include "entity_generated.h"
15 15
16class TestEventAdaptor : public Akonadi2::Domain::BufferAdaptor 16class TestFactory : public DomainTypeAdaptorFactory<Akonadi2::ApplicationDomain::Event, Akonadi2::ApplicationDomain::Buffer::Event, Akonadi2::ApplicationDomain::Buffer::Event, Akonadi2::ApplicationDomain::Buffer::EventBuilder, Akonadi2::ApplicationDomain::Buffer::EventBuilder>
17{
18public:
19 TestEventAdaptor()
20 : Akonadi2::Domain::BufferAdaptor()
21 {
22 }
23
24 void setProperty(const QString &key, const QVariant &value)
25 {
26 if (mResourceMapper->mWriteAccessors.contains(key)) {
27 // mResourceMapper.setProperty(key, value, mResourceBuffer);
28 } else {
29 // mLocalMapper.;
30 }
31 }
32
33 virtual QVariant getProperty(const QString &key) const
34 {
35 if (mResourceBuffer && mResourceMapper->mReadAccessors.contains(key)) {
36 return mResourceMapper->getProperty(key, mResourceBuffer);
37 } else if (mLocalBuffer) {
38 return mLocalMapper->getProperty(key, mLocalBuffer);
39 }
40 return QVariant();
41 }
42
43 Akonadi2::Domain::Buffer::Event const *mLocalBuffer;
44 Akonadi2::Domain::Buffer::Event const *mResourceBuffer;
45
46 QSharedPointer<PropertyMapper<Akonadi2::Domain::Buffer::Event> > mLocalMapper;
47 QSharedPointer<PropertyMapper<Akonadi2::Domain::Buffer::Event> > mResourceMapper;
48};
49
50class TestFactory : public DomainTypeAdaptorFactory<Akonadi2::Domain::Event, Akonadi2::Domain::Buffer::Event, Akonadi2::Domain::Buffer::Event>
51{ 17{
52public: 18public:
53 TestFactory() 19 TestFactory()
54 { 20 {
55 mResourceMapper = QSharedPointer<PropertyMapper<Akonadi2::Domain::Buffer::Event> >::create(); 21 mResourceWriteMapper = initializeWritePropertyMapper<Akonadi2::ApplicationDomain::Buffer::EventBuilder>();
56 mResourceMapper->mReadAccessors.insert("summary", [](Akonadi2::Domain::Buffer::Event const *buffer) -> QVariant {
57 if (buffer->summary()) {
58 return QString::fromStdString(buffer->summary()->c_str());
59 }
60 return QVariant();
61 });
62 }
63
64 virtual QSharedPointer<Akonadi2::Domain::BufferAdaptor> createAdaptor(const Akonadi2::Entity &entity)
65 {
66 Akonadi2::Domain::Buffer::Event const *resourceBuffer = 0;
67 if (auto resourceData = entity.resource()) {
68 flatbuffers::Verifier verifyer(resourceData->Data(), resourceData->size());
69 if (Akonadi2::Domain::Buffer::VerifyEventBuffer(verifyer)) {
70 resourceBuffer = Akonadi2::Domain::Buffer::GetEvent(resourceData->Data());
71 if (resourceBuffer->summary()) {
72 qDebug() << QString::fromStdString(std::string(resourceBuffer->summary()->c_str()));
73 }
74 }
75 }
76
77 // Akonadi2::Metadata const *metadataBuffer = 0;
78 // if (auto metadataData = entity.metadata()) {
79 // flatbuffers::Verifier verifyer(metadataData->Data(), metadataData->size());
80 // if (Akonadi2::VerifyMetadataBuffer(verifyer)) {
81 // metadataBuffer = Akonadi2::GetMetadata(metadataData);
82 // }
83 // }
84
85 Akonadi2::Domain::Buffer::Event const *localBuffer = 0;
86 if (auto localData = entity.local()) {
87 flatbuffers::Verifier verifyer(localData->Data(), localData->size());
88 if (Akonadi2::Domain::Buffer::VerifyEventBuffer(verifyer)) {
89 localBuffer = Akonadi2::Domain::Buffer::GetEvent(localData);
90 }
91 }
92
93 auto adaptor = QSharedPointer<TestEventAdaptor>::create();
94 adaptor->mLocalBuffer = localBuffer;
95 adaptor->mResourceBuffer = resourceBuffer;
96 adaptor->mResourceMapper = mResourceMapper;
97 adaptor->mLocalMapper = mLocalMapper;
98 return adaptor;
99 } 22 }
100}; 23};
101 24
@@ -111,6 +34,21 @@ private Q_SLOTS:
111 { 34 {
112 } 35 }
113 36
37 void testCreateBufferPart()
38 {
39 auto writeMapper = initializeWritePropertyMapper<Akonadi2::ApplicationDomain::Buffer::EventBuilder>();
40
41 Akonadi2::ApplicationDomain::Event event;
42 event.setProperty("summary", "foo");
43
44 flatbuffers::FlatBufferBuilder fbb;
45 auto pos = createBufferPart<Akonadi2::ApplicationDomain::Buffer::EventBuilder, Akonadi2::ApplicationDomain::Buffer::Event>(event, fbb, *writeMapper);
46 Akonadi2::ApplicationDomain::Buffer::FinishEventBuffer(fbb, pos);
47
48 flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
49 QVERIFY(verifier.VerifyBuffer<Akonadi2::ApplicationDomain::Buffer::Event>());
50 }
51
114 void testAdaptor() 52 void testAdaptor()
115 { 53 {
116 //Create entity buffer 54 //Create entity buffer
@@ -127,12 +65,12 @@ private Q_SLOTS:
127 static uint8_t rawData[100]; 65 static uint8_t rawData[100];
128 auto attachment = m_fbb.CreateVector(rawData, 100); 66 auto attachment = m_fbb.CreateVector(rawData, 100);
129 67
130 auto builder = Akonadi2::Domain::Buffer::EventBuilder(m_fbb); 68 auto builder = Akonadi2::ApplicationDomain::Buffer::EventBuilder(m_fbb);
131 builder.add_summary(summary); 69 builder.add_summary(summary);
132 builder.add_description(description); 70 builder.add_description(description);
133 builder.add_attachment(attachment); 71 builder.add_attachment(attachment);
134 auto buffer = builder.Finish(); 72 auto buffer = builder.Finish();
135 Akonadi2::Domain::Buffer::FinishEventBuffer(m_fbb, buffer); 73 Akonadi2::ApplicationDomain::Buffer::FinishEventBuffer(m_fbb, buffer);
136 74
137 flatbuffers::FlatBufferBuilder fbb; 75 flatbuffers::FlatBufferBuilder fbb;
138 Akonadi2::EntityBuffer::assembleEntityBuffer(fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize(), m_fbb.GetBufferPointer(), m_fbb.GetSize(), m_fbb.GetBufferPointer(), m_fbb.GetSize()); 76 Akonadi2::EntityBuffer::assembleEntityBuffer(fbb, metadataFbb.GetBufferPointer(), metadataFbb.GetSize(), m_fbb.GetBufferPointer(), m_fbb.GetSize(), m_fbb.GetBufferPointer(), m_fbb.GetSize());
diff --git a/tests/dummyresourcebenchmark.cpp b/tests/dummyresourcebenchmark.cpp
index 01d9ca4..d83cb70 100644
--- a/tests/dummyresourcebenchmark.cpp
+++ b/tests/dummyresourcebenchmark.cpp
@@ -3,10 +3,17 @@
3#include <QString> 3#include <QString>
4 4
5#include "dummyresource/resourcefactory.h" 5#include "dummyresource/resourcefactory.h"
6#include "dummyresource/domainadaptor.h"
6#include "clientapi.h" 7#include "clientapi.h"
7#include "commands.h" 8#include "commands.h"
8#include "entitybuffer.h" 9#include "entitybuffer.h"
9 10
11#include "event_generated.h"
12#include "entity_generated.h"
13#include "metadata_generated.h"
14#include "createentity_generated.h"
15#include <iostream>
16
10static void removeFromDisk(const QString &name) 17static void removeFromDisk(const QString &name)
11{ 18{
12 Akonadi2::Storage store(Akonadi2::Store::storageLocation(), name, Akonadi2::Storage::ReadWrite); 19 Akonadi2::Storage store(Akonadi2::Store::storageLocation(), name, Akonadi2::Storage::ReadWrite);
@@ -41,11 +48,11 @@ private Q_SLOTS:
41 time.start(); 48 time.start();
42 int num = 10000; 49 int num = 10000;
43 for (int i = 0; i < num; i++) { 50 for (int i = 0; i < num; i++) {
44 Akonadi2::Domain::Event event; 51 Akonadi2::ApplicationDomain::Event event;
45 event.setProperty("uid", "testuid"); 52 event.setProperty("uid", "testuid");
46 QCOMPARE(event.getProperty("uid").toByteArray(), QByteArray("testuid")); 53 QCOMPARE(event.getProperty("uid").toByteArray(), QByteArray("testuid"));
47 event.setProperty("summary", "summaryValue"); 54 event.setProperty("summary", "summaryValue");
48 Akonadi2::Store::create<Akonadi2::Domain::Event>(event, "org.kde.dummy"); 55 Akonadi2::Store::create<Akonadi2::ApplicationDomain::Event>(event, "org.kde.dummy");
49 } 56 }
50 auto appendTime = time.elapsed(); 57 auto appendTime = time.elapsed();
51 58
@@ -57,7 +64,7 @@ private Q_SLOTS:
57 query.processAll = true; 64 query.processAll = true;
58 65
59 query.propertyFilter.insert("uid", "nonexistantuid"); 66 query.propertyFilter.insert("uid", "nonexistantuid");
60 async::SyncListResult<Akonadi2::Domain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::Domain::Event>(query)); 67 async::SyncListResult<Akonadi2::ApplicationDomain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::ApplicationDomain::Event>(query));
61 result.exec(); 68 result.exec();
62 } 69 }
63 auto allProcessedTime = time.elapsed(); 70 auto allProcessedTime = time.elapsed();
@@ -71,7 +78,7 @@ private Q_SLOTS:
71 query.processAll = false; 78 query.processAll = false;
72 79
73 query.propertyFilter.insert("uid", "testuid"); 80 query.propertyFilter.insert("uid", "testuid");
74 async::SyncListResult<Akonadi2::Domain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::Domain::Event>(query)); 81 async::SyncListResult<Akonadi2::ApplicationDomain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::ApplicationDomain::Event>(query));
75 result.exec(); 82 result.exec();
76 QCOMPARE(result.size(), num); 83 QCOMPARE(result.size(), num);
77 } 84 }
@@ -79,6 +86,85 @@ private Q_SLOTS:
79 qDebug() << "All processed: " << allProcessedTime << "/sec " << num*1000/allProcessedTime; 86 qDebug() << "All processed: " << allProcessedTime << "/sec " << num*1000/allProcessedTime;
80 qDebug() << "Query Time: " << time.elapsed() << "/sec " << num*1000/time.elapsed(); 87 qDebug() << "Query Time: " << time.elapsed() << "/sec " << num*1000/time.elapsed();
81 } 88 }
89
90 void testWriteInProcess()
91 {
92 QTime time;
93 time.start();
94 int num = 10000;
95
96 Akonadi2::Pipeline pipeline("org.kde.dummy");
97 QSignalSpy revisionSpy(&pipeline, SIGNAL(revisionUpdated()));
98 DummyResource resource;
99 resource.configurePipeline(&pipeline);
100
101 flatbuffers::FlatBufferBuilder eventFbb;
102 eventFbb.Clear();
103 {
104 auto summary = eventFbb.CreateString("summary");
105 Akonadi2::ApplicationDomain::Buffer::EventBuilder eventBuilder(eventFbb);
106 eventBuilder.add_summary(summary);
107 auto eventLocation = eventBuilder.Finish();
108 Akonadi2::ApplicationDomain::Buffer::FinishEventBuffer(eventFbb, eventLocation);
109 }
110
111 flatbuffers::FlatBufferBuilder localFbb;
112 {
113 auto uid = localFbb.CreateString("testuid");
114 auto localBuilder = Akonadi2::ApplicationDomain::Buffer::EventBuilder(localFbb);
115 localBuilder.add_uid(uid);
116 auto location = localBuilder.Finish();
117 Akonadi2::ApplicationDomain::Buffer::FinishEventBuffer(localFbb, location);
118 }
119
120 flatbuffers::FlatBufferBuilder entityFbb;
121 Akonadi2::EntityBuffer::assembleEntityBuffer(entityFbb, 0, 0, eventFbb.GetBufferPointer(), eventFbb.GetSize(), localFbb.GetBufferPointer(), localFbb.GetSize());
122
123 flatbuffers::FlatBufferBuilder fbb;
124 auto type = fbb.CreateString(Akonadi2::ApplicationDomain::getTypeName<Akonadi2::ApplicationDomain::Event>().toStdString().data());
125 auto delta = fbb.CreateVector<uint8_t>(entityFbb.GetBufferPointer(), entityFbb.GetSize());
126 Akonadi2::Commands::CreateEntityBuilder builder(fbb);
127 builder.add_domainType(type);
128 builder.add_delta(delta);
129 auto location = builder.Finish();
130 Akonadi2::Commands::FinishCreateEntityBuffer(fbb, location);
131
132 const QByteArray command(reinterpret_cast<const char *>(fbb.GetBufferPointer()), fbb.GetSize());
133
134 for (int i = 0; i < num; i++) {
135 resource.processCommand(Akonadi2::Commands::CreateEntityCommand, command, command.size(), &pipeline);
136 }
137 auto appendTime = time.elapsed();
138
139 //Wait until all messages have been processed
140 resource.processAllMessages().exec().waitForFinished();
141
142 auto allProcessedTime = time.elapsed();
143
144 std::cout << "Append to messagequeue " << appendTime << std::endl;
145 std::cout << "All processed: " << allProcessedTime << "/sec " << num*1000/allProcessedTime << std::endl;
146 }
147
148 void testCreateCommand()
149 {
150 Akonadi2::ApplicationDomain::Event event;
151
152 QBENCHMARK {
153 auto mFactory = new DummyEventAdaptorFactory;
154 static flatbuffers::FlatBufferBuilder entityFbb;
155 entityFbb.Clear();
156 mFactory->createBuffer(event, entityFbb);
157
158 static flatbuffers::FlatBufferBuilder fbb;
159 fbb.Clear();
160 //This is the resource buffer type and not the domain type
161 auto type = fbb.CreateString("event");
162 // auto delta = fbb.CreateVector<uint8_t>(entityFbb.GetBufferPointer(), entityFbb.GetSize());
163 auto delta = Akonadi2::EntityBuffer::appendAsVector(fbb, entityFbb.GetBufferPointer(), entityFbb.GetSize());
164 auto location = Akonadi2::Commands::CreateCreateEntity(fbb, type, delta);
165 Akonadi2::Commands::FinishCreateEntityBuffer(fbb, location);
166 }
167 }
82}; 168};
83 169
84QTEST_MAIN(DummyResourceBenchmark) 170QTEST_MAIN(DummyResourceBenchmark)
diff --git a/tests/dummyresourcetest.cpp b/tests/dummyresourcetest.cpp
index b185664..5fed7cd 100644
--- a/tests/dummyresourcetest.cpp
+++ b/tests/dummyresourcetest.cpp
@@ -49,26 +49,26 @@ private Q_SLOTS:
49 eventFbb.Clear(); 49 eventFbb.Clear();
50 { 50 {
51 auto summary = eventFbb.CreateString("summary"); 51 auto summary = eventFbb.CreateString("summary");
52 Akonadi2::Domain::Buffer::EventBuilder eventBuilder(eventFbb); 52 Akonadi2::ApplicationDomain::Buffer::EventBuilder eventBuilder(eventFbb);
53 eventBuilder.add_summary(summary); 53 eventBuilder.add_summary(summary);
54 auto eventLocation = eventBuilder.Finish(); 54 auto eventLocation = eventBuilder.Finish();
55 Akonadi2::Domain::Buffer::FinishEventBuffer(eventFbb, eventLocation); 55 Akonadi2::ApplicationDomain::Buffer::FinishEventBuffer(eventFbb, eventLocation);
56 } 56 }
57 57
58 flatbuffers::FlatBufferBuilder localFbb; 58 flatbuffers::FlatBufferBuilder localFbb;
59 { 59 {
60 auto uid = localFbb.CreateString("testuid"); 60 auto uid = localFbb.CreateString("testuid");
61 auto localBuilder = Akonadi2::Domain::Buffer::EventBuilder(localFbb); 61 auto localBuilder = Akonadi2::ApplicationDomain::Buffer::EventBuilder(localFbb);
62 localBuilder.add_uid(uid); 62 localBuilder.add_uid(uid);
63 auto location = localBuilder.Finish(); 63 auto location = localBuilder.Finish();
64 Akonadi2::Domain::Buffer::FinishEventBuffer(localFbb, location); 64 Akonadi2::ApplicationDomain::Buffer::FinishEventBuffer(localFbb, location);
65 } 65 }
66 66
67 flatbuffers::FlatBufferBuilder entityFbb; 67 flatbuffers::FlatBufferBuilder entityFbb;
68 Akonadi2::EntityBuffer::assembleEntityBuffer(entityFbb, 0, 0, eventFbb.GetBufferPointer(), eventFbb.GetSize(), localFbb.GetBufferPointer(), localFbb.GetSize()); 68 Akonadi2::EntityBuffer::assembleEntityBuffer(entityFbb, 0, 0, eventFbb.GetBufferPointer(), eventFbb.GetSize(), localFbb.GetBufferPointer(), localFbb.GetSize());
69 69
70 flatbuffers::FlatBufferBuilder fbb; 70 flatbuffers::FlatBufferBuilder fbb;
71 auto type = fbb.CreateString(Akonadi2::Domain::getTypeName<Akonadi2::Domain::Event>().toStdString().data()); 71 auto type = fbb.CreateString(Akonadi2::ApplicationDomain::getTypeName<Akonadi2::ApplicationDomain::Event>().toStdString().data());
72 auto delta = fbb.CreateVector<uint8_t>(entityFbb.GetBufferPointer(), entityFbb.GetSize()); 72 auto delta = fbb.CreateVector<uint8_t>(entityFbb.GetBufferPointer(), entityFbb.GetSize());
73 Akonadi2::Commands::CreateEntityBuilder builder(fbb); 73 Akonadi2::Commands::CreateEntityBuilder builder(fbb);
74 builder.add_domainType(type); 74 builder.add_domainType(type);
@@ -98,18 +98,18 @@ private Q_SLOTS:
98 98
99 void testProperty() 99 void testProperty()
100 { 100 {
101 Akonadi2::Domain::Event event; 101 Akonadi2::ApplicationDomain::Event event;
102 event.setProperty("uid", "testuid"); 102 event.setProperty("uid", "testuid");
103 QCOMPARE(event.getProperty("uid").toByteArray(), QByteArray("testuid")); 103 QCOMPARE(event.getProperty("uid").toByteArray(), QByteArray("testuid"));
104 } 104 }
105 105
106 void testWriteToFacadeAndQueryByUid() 106 void testWriteToFacadeAndQueryByUid()
107 { 107 {
108 Akonadi2::Domain::Event event; 108 Akonadi2::ApplicationDomain::Event event;
109 event.setProperty("uid", "testuid"); 109 event.setProperty("uid", "testuid");
110 QCOMPARE(event.getProperty("uid").toByteArray(), QByteArray("testuid")); 110 QCOMPARE(event.getProperty("uid").toByteArray(), QByteArray("testuid"));
111 event.setProperty("summary", "summaryValue"); 111 event.setProperty("summary", "summaryValue");
112 Akonadi2::Store::create<Akonadi2::Domain::Event>(event, "org.kde.dummy"); 112 Akonadi2::Store::create<Akonadi2::ApplicationDomain::Event>(event, "org.kde.dummy");
113 113
114 Akonadi2::Query query; 114 Akonadi2::Query query;
115 query.resources << "org.kde.dummy"; 115 query.resources << "org.kde.dummy";
@@ -117,7 +117,7 @@ private Q_SLOTS:
117 query.processAll = true; 117 query.processAll = true;
118 118
119 query.propertyFilter.insert("uid", "testuid"); 119 query.propertyFilter.insert("uid", "testuid");
120 async::SyncListResult<Akonadi2::Domain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::Domain::Event>(query)); 120 async::SyncListResult<Akonadi2::ApplicationDomain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::ApplicationDomain::Event>(query));
121 result.exec(); 121 result.exec();
122 QCOMPARE(result.size(), 1); 122 QCOMPARE(result.size(), 1);
123 auto value = result.first(); 123 auto value = result.first();
@@ -147,7 +147,7 @@ private Q_SLOTS:
147 query.syncOnDemand = true; 147 query.syncOnDemand = true;
148 query.processAll = true; 148 query.processAll = true;
149 149
150 async::SyncListResult<Akonadi2::Domain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::Domain::Event>(query)); 150 async::SyncListResult<Akonadi2::ApplicationDomain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::ApplicationDomain::Event>(query));
151 result.exec(); 151 result.exec();
152 QVERIFY(!result.isEmpty()); 152 QVERIFY(!result.isEmpty());
153 auto value = result.first(); 153 auto value = result.first();