diff options
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) | |||
36 | endfunction(generate_flatbuffers) | 36 | endfunction(generate_flatbuffers) |
37 | 37 | ||
38 | set(CMAKE_AUTOMOC ON) | 38 | set(CMAKE_AUTOMOC ON) |
39 | add_definitions("-Wall -std=c++0x") | 39 | add_definitions("-Wall -std=c++0x -g") |
40 | include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${FLATBUFFERS_INCLUDE_DIR} ${CMAKE_BINARY_DIR}/common) | 40 | include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${FLATBUFFERS_INCLUDE_DIR} ${CMAKE_BINARY_DIR}/common) |
41 | include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/common) | 41 | include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/common) |
42 | 42 | ||
43 | configure_file(hawd.conf hawd.conf) | 43 | configure_file(hawd.conf hawd.conf) |
44 | 44 | ||
45 | enable_testing() | ||
46 | |||
45 | set(AKONADI2_RESOURCE_PLUGINS_PATH ${QT_PLUGIN_INSTALL_DIR}/akonadi2/resources) | 47 | set(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 | |||
14 | namespace Akonadi2 | 14 | namespace Akonadi2 |
15 | { | 15 | { |
16 | 16 | ||
17 | namespace Domain | 17 | namespace ApplicationDomain |
18 | { | 18 | { |
19 | 19 | ||
20 | template<> | 20 | template<> |
21 | QString getTypeName<Event>() | 21 | QByteArray getTypeName<Event>() |
22 | { | 22 | { |
23 | return "event"; | 23 | return "event"; |
24 | } | 24 | } |
25 | 25 | ||
26 | template<> | 26 | template<> |
27 | QString getTypeName<Todo>() | 27 | QByteArray getTypeName<Todo>() |
28 | { | 28 | { |
29 | return "todo"; | 29 | return "todo"; |
30 | } | 30 | } |
31 | 31 | ||
32 | } // namespace Domain | 32 | } // namespace Domain |
33 | 33 | ||
34 | void Store::shutdown(const QString &identifier) | 34 | void 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 { | |||
163 | namespace Akonadi2 { | 163 | namespace 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 | */ |
175 | namespace Domain { | 175 | namespace 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 { | |||
180 | class BufferAdaptor { | 180 | class BufferAdaptor { |
181 | public: | 181 | public: |
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 | ||
188 | class MemoryBufferAdaptor : public BufferAdaptor { | 188 | class 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 | ||
209 | private: | 209 | private: |
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 | */ |
218 | class AkonadiDomainType { | 218 | class ApplicationDomainType { |
219 | public: | 219 | public: |
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 | ||
238 | private: | 240 | private: |
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 | ||
249 | struct Event : public AkonadiDomainType { | 251 | struct 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 | ||
254 | struct Todo : public AkonadiDomainType { | 256 | struct 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 | ||
259 | struct Calendar : public AkonadiDomainType { | 261 | struct 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 | ||
264 | class Mail : public AkonadiDomainType { | 266 | class Mail : public ApplicationDomainType { |
265 | }; | 267 | }; |
266 | 268 | ||
267 | class Folder : public AkonadiDomainType { | 269 | class Folder : public ApplicationDomainType { |
268 | }; | 270 | }; |
269 | 271 | ||
270 | /** | 272 | /** |
@@ -274,13 +276,13 @@ class Folder : public AkonadiDomainType { | |||
274 | */ | 276 | */ |
275 | 277 | ||
276 | template<class DomainType> | 278 | template<class DomainType> |
277 | QString getTypeName(); | 279 | QByteArray getTypeName(); |
278 | 280 | ||
279 | template<> | 281 | template<> |
280 | QString getTypeName<Event>(); | 282 | QByteArray getTypeName<Event>(); |
281 | 283 | ||
282 | template<> | 284 | template<> |
283 | QString getTypeName<Todo>(); | 285 | QByteArray 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 | */ |
302 | class Query | 307 | class Query |
303 | { | 308 | { |
304 | public: | 309 | public: |
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> | |||
328 | class StoreFacade { | 333 | class StoreFacade { |
329 | public: | 334 | public: |
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 | ||
394 | private: | 399 | private: |
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 @@ | |||
1 | namespace Akonadi2.Domain.Buffer; | 1 | namespace Akonadi2.ApplicationDomain.Buffer; |
2 | 2 | ||
3 | table Event { | 3 | table 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 | |||
22 | template <> | ||
23 | flatbuffers::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 | |||
31 | template <> | ||
32 | QSharedPointer<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 | |||
50 | template <> | ||
51 | QSharedPointer<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 | */ |
34 | template<typename BufferType> | 40 | template<typename BufferType> |
35 | class PropertyMapper | 41 | class ReadPropertyMapper |
42 | { | ||
43 | public: | ||
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 | } | ||
57 | private: | ||
58 | QHash<QByteArray, std::function<QVariant(BufferType const *)> > mReadAccessors; | ||
59 | }; | ||
60 | |||
61 | template<typename BufferBuilder> | ||
62 | class WritePropertyMapper | ||
36 | { | 63 | { |
37 | public: | 64 | public: |
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 | } | ||
76 | private: | ||
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 | */ | ||
84 | template <class T> | ||
85 | flatbuffers::uoffset_t extractProperty(const QVariant &, flatbuffers::FlatBufferBuilder &fbb); | ||
86 | |||
87 | /** | ||
88 | * Create a buffer from a domain object using the provided mappings | ||
89 | */ | ||
90 | template <class Builder, class Buffer> | ||
91 | flatbuffers::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 | */ | ||
118 | template <class LocalBuffer, class ResourceBuffer> | ||
119 | class GenericBufferAdaptor : public Akonadi2::ApplicationDomain::BufferAdaptor | ||
120 | { | ||
121 | public: | ||
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 | // { | 163 | template <class T> |
64 | // }; | 164 | QSharedPointer<ReadPropertyMapper<T> > initializeReadPropertyMapper(); |
165 | |||
166 | template <class T> | ||
167 | QSharedPointer<WritePropertyMapper<T> > initializeWritePropertyMapper(); | ||
65 | 168 | ||
66 | template<typename DomainType, typename LocalBuffer, typename ResourceBuffer> | 169 | /** |
67 | class 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 | */ | ||
174 | template<typename DomainType, typename LocalBuffer, typename ResourceBuffer, typename LocalBuilder, typename ResourceBuilder> | ||
175 | class DomainTypeAdaptorFactory | ||
68 | { | 176 | { |
69 | public: | 177 | public: |
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 | ||
74 | protected: | 207 | protected: |
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 | ||
59 | void EntityBuffer::assembleEntityBuffer(flatbuffers::FlatBufferBuilder &fbb, void const *metadataData, size_t metadataSize, void const *resourceData, size_t resourceSize, void const *localData, size_t localSize) | 59 | flatbuffers::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(); | 71 | void 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 | ||
20 | private: | 46 | private: |
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 | |||
33 | namespace Akonadi2 { | ||
34 | class ResourceAccess; | ||
35 | /** | ||
36 | * Default facade implementation for resources that are implemented in a separate process using the ResourceAccess class. | ||
37 | */ | ||
38 | template <typename DomainType> | ||
39 | class GenericFacade: public Akonadi2::StoreFacade<DomainType> | ||
40 | { | ||
41 | public: | ||
42 | GenericFacade(const QByteArray &resourceIdentifier) | ||
43 | : Akonadi2::StoreFacade<DomainType>(), | ||
44 | mResourceAccess(new ResourceAccess(resourceIdentifier)) | ||
45 | { | ||
46 | } | ||
47 | |||
48 | ~GenericFacade() | ||
49 | { | ||
50 | } | ||
51 | |||
52 | protected: | ||
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 | |||
83 | private: | ||
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 @@ | |||
26 | namespace Akonadi2 | 26 | namespace Akonadi2 |
27 | { | 27 | { |
28 | 28 | ||
29 | /** | ||
30 | * Resource interface | ||
31 | */ | ||
29 | class AKONADI2COMMON_EXPORT Resource | 32 | class AKONADI2COMMON_EXPORT Resource |
30 | { | 33 | { |
31 | public: | 34 | public: |
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 | */ | ||
47 | class ResourceFactory : public QObject | 52 | class ResourceFactory : public QObject |
48 | { | 53 | { |
49 | public: | 54 | public: |
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: | |||
64 | class ResourceAccess::Private | 64 | class ResourceAccess::Private |
65 | { | 65 | { |
66 | public: | 66 | public: |
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 | ||
80 | ResourceAccess::Private::Private(const QString &name, ResourceAccess *q) | 80 | ResourceAccess::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 | ||
90 | ResourceAccess::ResourceAccess(const QString &resourceName, QObject *parent) | 90 | ResourceAccess::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 | ||
115 | QString ResourceAccess::resourceName() const | 115 | QByteArray 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 | ||
37 | public: | 37 | public: |
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 | ||
49 | std::function<void(const Storage::Error &error)> Storage::defaultErrorHandler() | 49 | std::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 | |||
425 | void Storage::removeFromDisk() const | 425 | void 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 | ||
7 | class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::Domain::Event> | 7 | class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::ApplicationDomain::Event> |
8 | { | 8 | { |
9 | public: | 9 | public: |
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 | ||
28 | class ClientAPITest : public QObject | 28 | class 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 | ||
14 | using namespace DummyCalendar; | 15 | using namespace DummyCalendar; |
15 | using namespace flatbuffers; | 16 | using namespace flatbuffers; |
16 | 17 | ||
17 | using namespace DummyCalendar; | ||
18 | using namespace flatbuffers; | ||
19 | |||
20 | //This will become a generic implementation that simply takes the resource buffer and local buffer pointer | ||
21 | class DummyEventAdaptor : public Akonadi2::Domain::BufferAdaptor | ||
22 | { | ||
23 | public: | ||
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 | ||
66 | DummyEventAdaptorFactory::DummyEventAdaptorFactory() | 21 | DummyEventAdaptorFactory::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 | |
93 | QSharedPointer<Akonadi2::Domain::BufferAdaptor> DummyEventAdaptorFactory::createAdaptor(const Akonadi2::Entity &entity) | 39 | void 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 | |||
127 | void 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 | ||
9 | class DummyEventAdaptorFactory : public DomainTypeAdaptorFactory<Akonadi2::Domain::Event, Akonadi2::Domain::Buffer::Event, DummyCalendar::DummyEvent> | 8 | class DummyEventAdaptorFactory : public DomainTypeAdaptorFactory<Akonadi2::ApplicationDomain::Event, Akonadi2::ApplicationDomain::Buffer::Event, DummyCalendar::DummyEvent, Akonadi2::ApplicationDomain::Buffer::EventBuilder, DummyCalendar::DummyEventBuilder> |
10 | { | 9 | { |
11 | public: | 10 | public: |
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 @@ | |||
37 | using namespace DummyCalendar; | 36 | using namespace DummyCalendar; |
38 | using namespace flatbuffers; | 37 | using namespace flatbuffers; |
39 | 38 | ||
39 | |||
40 | DummyResourceFacade::DummyResourceFacade() | 40 | DummyResourceFacade::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 | ||
51 | Async::Job<void> DummyResourceFacade::create(const Akonadi2::Domain::Event &domainObject) | 50 | Async::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 | ||
66 | Async::Job<void> DummyResourceFacade::modify(const Akonadi2::Domain::Event &domainObject) | 57 | Async::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 | ||
72 | Async::Job<void> DummyResourceFacade::remove(const Akonadi2::Domain::Event &domainObject) | 63 | Async::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 | ||
78 | static std::function<bool(const std::string &key, DummyEvent const *buffer, Akonadi2::Domain::Buffer::Event const *local)> prepareQuery(const Akonadi2::Query &query) | 69 | static 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 | ||
118 | Async::Job<void> DummyResourceFacade::synchronizeResource(bool sync, bool processAll) | 109 | void 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 | |||
136 | void 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 | ||
196 | Async::Job<void> DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback) | 150 | Async::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 | ||
30 | namespace Akonadi2 { | 32 | class DummyResourceFacade : public Akonadi2::GenericFacade<Akonadi2::ApplicationDomain::Event> |
31 | class ResourceAccess; | ||
32 | } | ||
33 | |||
34 | |||
35 | class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::Domain::Event> | ||
36 | { | 33 | { |
37 | public: | 34 | public: |
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 | ||
45 | private: | 42 | private: |
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 | ||
290 | void DummyResource::onProcessorError(int errorCode, const QString &errorMessage) | 279 | void 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 | |||
325 | void DummyResource::enqueueCommand(MessageQueue &mq, int commandId, const QByteArray &data) | 314 | void 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 | ||
389 | Async::Job<void> DummyResource::processAllMessages() | 375 | Async::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 | ||
426 | void DummyResourceFactory::registerFacades(Akonadi2::FacadeFactory &factory) | 418 | void 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 | ||
38 | Listener::Listener(const QString &resourceName, QObject *parent) | 38 | Listener::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 | ||
60 | public: | 60 | public: |
61 | Listener(const QString &resourceName, QObject *parent = 0); | 61 | Listener(const QByteArray &resourceName, QObject *parent = 0); |
62 | ~Listener(); | 62 | ~Listener(); |
63 | 63 | ||
64 | Q_SIGNALS: | 64 | Q_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 | ||
3 | set(CMAKE_AUTOMOC ON) | 3 | set(CMAKE_AUTOMOC ON) |
4 | include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/hawd) | 4 | include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/hawd) |
5 | include_directories (${CMAKE_CURRENT_BINARY_DIR}/../dummyresource) | ||
5 | 6 | ||
6 | generate_flatbuffers(calendar) | 7 | generate_flatbuffers(calendar) |
7 | 8 | ||
@@ -13,14 +14,26 @@ macro(manual_tests) | |||
13 | endforeach(_testname) | 14 | endforeach(_testname) |
14 | endmacro(manual_tests) | 15 | endmacro(manual_tests) |
15 | 16 | ||
17 | macro(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) | ||
24 | endmacro(auto_tests) | ||
25 | |||
16 | manual_tests ( | 26 | manual_tests ( |
17 | storagebenchmark | 27 | storagebenchmark |
28 | dummyresourcebenchmark | ||
29 | ) | ||
30 | |||
31 | auto_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 | ||
26 | target_link_libraries(dummyresourcetest akonadi2_resource_dummy) | 39 | target_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 | ||
16 | class TestEventAdaptor : public Akonadi2::Domain::BufferAdaptor | 16 | class TestFactory : public DomainTypeAdaptorFactory<Akonadi2::ApplicationDomain::Event, Akonadi2::ApplicationDomain::Buffer::Event, Akonadi2::ApplicationDomain::Buffer::Event, Akonadi2::ApplicationDomain::Buffer::EventBuilder, Akonadi2::ApplicationDomain::Buffer::EventBuilder> |
17 | { | ||
18 | public: | ||
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 | |||
50 | class TestFactory : public DomainTypeAdaptorFactory<Akonadi2::Domain::Event, Akonadi2::Domain::Buffer::Event, Akonadi2::Domain::Buffer::Event> | ||
51 | { | 17 | { |
52 | public: | 18 | public: |
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 | |||
10 | static void removeFromDisk(const QString &name) | 17 | static 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 | ||
84 | QTEST_MAIN(DummyResourceBenchmark) | 170 | QTEST_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(); |