summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/genericresource.cpp67
-rw-r--r--common/genericresource.h8
-rw-r--r--examples/maildirresource/maildirresource.cpp172
-rw-r--r--examples/maildirresource/maildirresource.h3
4 files changed, 152 insertions, 98 deletions
diff --git a/common/genericresource.cpp b/common/genericresource.cpp
index 637e371..eae6ead 100644
--- a/common/genericresource.cpp
+++ b/common/genericresource.cpp
@@ -19,6 +19,10 @@
19#include <QDataStream> 19#include <QDataStream>
20#include <QTime> 20#include <QTime>
21 21
22//This is the resources entity type, and not the domain type
23#define ENTITY_TYPE_MAIL "mail"
24#define ENTITY_TYPE_FOLDER "folder"
25
22static int sBatchSize = 100; 26static int sBatchSize = 100;
23// This interval directly affects the roundtrip time of single commands 27// This interval directly affects the roundtrip time of single commands
24static int sCommitInterval = 10; 28static int sCommitInterval = 10;
@@ -392,11 +396,72 @@ void GenericResource::addType(const QByteArray &type, DomainTypeAdaptorFactoryIn
392{ 396{
393 mPipeline->setPreprocessors(type, preprocessors); 397 mPipeline->setPreprocessors(type, preprocessors);
394 mPipeline->setAdaptorFactory(type, factory); 398 mPipeline->setAdaptorFactory(type, factory);
399 mAdaptorFactories.insert(type, factory);
395} 400}
396 401
397KAsync::Job<void> GenericResource::replay(Sink::Storage &synchronizationStore, const QByteArray &type, const QByteArray &key, const QByteArray &value) 402KAsync::Job<void> GenericResource::replay(Sink::Storage &synchronizationStore, const QByteArray &type, const QByteArray &key, const QByteArray &value)
398{ 403{
399 return KAsync::null<void>(); 404 Sink::EntityBuffer buffer(value);
405 const Sink::Entity &entity = buffer.entity();
406 const auto metadataBuffer = Sink::EntityBuffer::readBuffer<Sink::Metadata>(entity.metadata());
407 Q_ASSERT(metadataBuffer);
408 if (!metadataBuffer->replayToSource()) {
409 Trace() << "Change is coming from the source";
410 return KAsync::null<void>();
411 }
412 const qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1;
413 const auto operation = metadataBuffer ? metadataBuffer->operation() : Sink::Operation_Creation;
414 const auto uid = Sink::Storage::uidFromKey(key);
415 QByteArray oldRemoteId;
416
417 if (operation != Sink::Operation_Creation) {
418 auto synchronizationTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadOnly);
419 oldRemoteId = resolveLocalId(type, uid, synchronizationTransaction);
420 }
421 Trace() << "Replaying " << key << type;
422
423 KAsync::Job<QByteArray> job = KAsync::null<QByteArray>();
424 if (type == ENTITY_TYPE_FOLDER) {
425 const Sink::ApplicationDomain::Folder folder(mResourceInstanceIdentifier, uid, revision, mAdaptorFactories.value(type)->createAdaptor(entity));
426 job = replay(folder, operation, oldRemoteId);
427 } else if (type == ENTITY_TYPE_MAIL) {
428 const Sink::ApplicationDomain::Mail mail(mResourceInstanceIdentifier, uid, revision, mAdaptorFactories.value(type)->createAdaptor(entity));
429 job = replay(mail, operation, oldRemoteId);
430 }
431
432 return job.then<void, QByteArray>([=, &synchronizationStore](const QByteArray &remoteId) {
433 auto synchronizationTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadWrite);
434 Trace() << "Replayed change with remote id: " << remoteId;
435 if (operation == Sink::Operation_Creation) {
436 if (remoteId.isEmpty()) {
437 Warning() << "Returned an empty remoteId from the creation";
438 } else {
439 recordRemoteId(type, uid, remoteId, synchronizationTransaction);
440 }
441 } else if (operation == Sink::Operation_Modification) {
442 if (remoteId.isEmpty()) {
443 Warning() << "Returned an empty remoteId from the creation";
444 } else {
445 updateRemoteId(type, uid, remoteId, synchronizationTransaction);
446 }
447 } else if (operation == Sink::Operation_Removal) {
448 removeRemoteId(type, uid, remoteId, synchronizationTransaction);
449 } else {
450 Warning() << "Unkown operation" << operation;
451 }
452 }, [](int errorCode, const QString &errorMessage) {
453 Warning() << "Failed to replay change: " << errorMessage;
454 });
455}
456
457KAsync::Job<QByteArray> GenericResource::replay(const ApplicationDomain::Mail &, Sink::Operation, const QByteArray &)
458{
459 return KAsync::null<QByteArray>();
460}
461
462KAsync::Job<QByteArray> GenericResource::replay(const ApplicationDomain::Folder &, Sink::Operation, const QByteArray &)
463{
464 return KAsync::null<QByteArray>();
400} 465}
401 466
402void GenericResource::removeDataFromDisk() 467void GenericResource::removeDataFromDisk()
diff --git a/common/genericresource.h b/common/genericresource.h
index 9582f06..c551e29 100644
--- a/common/genericresource.h
+++ b/common/genericresource.h
@@ -61,8 +61,15 @@ private slots:
61 61
62protected: 62protected:
63 void enableChangeReplay(bool); 63 void enableChangeReplay(bool);
64
64 void addType(const QByteArray &type, DomainTypeAdaptorFactoryInterface::Ptr factory, const QVector<Sink::Preprocessor *> &preprocessors); 65 void addType(const QByteArray &type, DomainTypeAdaptorFactoryInterface::Ptr factory, const QVector<Sink::Preprocessor *> &preprocessors);
66
67 ///Base implementation call the replay$Type calls
65 virtual KAsync::Job<void> replay(Sink::Storage &synchronizationStore, const QByteArray &type, const QByteArray &key, const QByteArray &value); 68 virtual KAsync::Job<void> replay(Sink::Storage &synchronizationStore, const QByteArray &type, const QByteArray &key, const QByteArray &value);
69 ///Implement to write back changes to the server
70 virtual KAsync::Job<QByteArray> replay(const Sink::ApplicationDomain::Mail &, Sink::Operation, const QByteArray &oldRemoteId);
71 virtual KAsync::Job<QByteArray> replay(const Sink::ApplicationDomain::Folder &, Sink::Operation, const QByteArray &oldRemoteId);
72
66 void onProcessorError(int errorCode, const QString &errorMessage); 73 void onProcessorError(int errorCode, const QString &errorMessage);
67 void enqueueCommand(MessageQueue &mq, int commandId, const QByteArray &data); 74 void enqueueCommand(MessageQueue &mq, int commandId, const QByteArray &data);
68 75
@@ -127,5 +134,6 @@ private:
127 int mError; 134 int mError;
128 QTimer mCommitQueueTimer; 135 QTimer mCommitQueueTimer;
129 qint64 mClientLowerBoundRevision; 136 qint64 mClientLowerBoundRevision;
137 QHash<QByteArray, DomainTypeAdaptorFactoryInterface::Ptr> mAdaptorFactories;
130}; 138};
131} 139}
diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp
index e1e4d95..3f6ae54 100644
--- a/examples/maildirresource/maildirresource.cpp
+++ b/examples/maildirresource/maildirresource.cpp
@@ -49,6 +49,8 @@
49#undef DEBUG_AREA 49#undef DEBUG_AREA
50#define DEBUG_AREA "resource.maildir" 50#define DEBUG_AREA "resource.maildir"
51 51
52using namespace Sink;
53
52static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath) 54static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath)
53{ 55{
54 auto parts = mimeMessagePath.split('/'); 56 auto parts = mimeMessagePath.split('/');
@@ -373,108 +375,86 @@ KAsync::Job<void> MaildirResource::synchronizeWithSource(Sink::Storage &mainStor
373 }); 375 });
374} 376}
375 377
376KAsync::Job<void> MaildirResource::replay(Sink::Storage &synchronizationStore, const QByteArray &type, const QByteArray &key, const QByteArray &value) 378KAsync::Job<QByteArray> MaildirResource::replay(const ApplicationDomain::Mail &mail, Sink::Operation operation, const QByteArray &oldRemoteId)
377{ 379{
378 auto synchronizationTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadWrite); 380 if (operation == Sink::Operation_Creation) {
379 381 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
380 Sink::EntityBuffer buffer(value); 382 Trace() << "Mail created: " << remoteId;
381 const Sink::Entity &entity = buffer.entity(); 383 return KAsync::start<QByteArray>([=]() -> QByteArray {
382 const auto metadataBuffer = Sink::EntityBuffer::readBuffer<Sink::Metadata>(entity.metadata()); 384 return remoteId.toUtf8();
383 Q_ASSERT(metadataBuffer); 385 });
384 if (!metadataBuffer->replayToSource()) { 386 } else if (operation == Sink::Operation_Removal) {
385 Trace() << "Change is coming from the source"; 387 Trace() << "Removing a mail: " << oldRemoteId;
386 return KAsync::null<void>(); 388 QFile::remove(oldRemoteId);
387 } 389 return KAsync::null<QByteArray>();
388 const qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; 390 } else if (operation == Sink::Operation_Modification) {
389 const auto operation = metadataBuffer ? metadataBuffer->operation() : Sink::Operation_Creation; 391 Trace() << "Modifying a mail: " << oldRemoteId;
390
391 Trace() << "Replaying " << key << type;
392 if (type == ENTITY_TYPE_FOLDER) {
393 if (operation == Sink::Operation_Creation) {
394 const Sink::ApplicationDomain::Folder folder(mResourceInstanceIdentifier, Sink::Storage::uidFromKey(key), revision, mFolderAdaptorFactory->createAdaptor(entity));
395 auto folderName = folder.getProperty("name").toString();
396 //TODO handle non toplevel folders
397 auto path = mMaildirPath + "/" + folderName;
398 Trace() << "Creating a new folder: " << path;
399 KPIM::Maildir maildir(path, false);
400 maildir.create();
401 recordRemoteId(ENTITY_TYPE_FOLDER, folder.identifier(), path.toUtf8(), synchronizationTransaction);
402 } else if (operation == Sink::Operation_Removal) {
403 const auto uid = Sink::Storage::uidFromKey(key);
404 const auto remoteId = resolveLocalId(ENTITY_TYPE_FOLDER, uid, synchronizationTransaction);
405 const auto path = remoteId;
406 Trace() << "Removing a folder: " << path;
407 KPIM::Maildir maildir(path, false);
408 maildir.remove();
409 removeRemoteId(ENTITY_TYPE_FOLDER, uid, remoteId, synchronizationTransaction);
410 } else if (operation == Sink::Operation_Modification) {
411 Warning() << "Folder modifications are not implemented";
412 } else {
413 Warning() << "Unkown operation" << operation;
414 }
415 } else if (type == ENTITY_TYPE_MAIL) {
416 if (operation == Sink::Operation_Creation) {
417 const Sink::ApplicationDomain::Mail mail(mResourceInstanceIdentifier, Sink::Storage::uidFromKey(key), revision, mMailAdaptorFactory->createAdaptor(entity));
418 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
419 Trace() << "Creating a new mail." << remoteId;
420 if (remoteId.isEmpty()) {
421 Warning() << "Failed to create mail: " << remoteId;
422 return KAsync::error<void>(1, "Failed to create mail.");
423 } else {
424 Trace() << "Mail created: " << remoteId;
425 recordRemoteId(ENTITY_TYPE_MAIL, mail.identifier(), remoteId.toUtf8(), synchronizationTransaction);
426 }
427 } else if (operation == Sink::Operation_Removal) {
428 const auto uid = Sink::Storage::uidFromKey(key);
429 const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, uid, synchronizationTransaction);
430 Trace() << "Removing a mail: " << remoteId;
431 QFile::remove(remoteId);
432 removeRemoteId(ENTITY_TYPE_MAIL, uid, remoteId, synchronizationTransaction);
433 } else if (operation == Sink::Operation_Modification) {
434 const auto uid = Sink::Storage::uidFromKey(key);
435 const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, uid, synchronizationTransaction);
436 Trace() << "Modifying a mail: " << remoteId;
437
438 const Sink::ApplicationDomain::Mail mail(mResourceInstanceIdentifier, Sink::Storage::uidFromKey(key), revision, mMailAdaptorFactory->createAdaptor(entity));
439
440 const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
441 const auto maildirPath = KPIM::Maildir::getDirectoryFromFile(filePath);
442 KPIM::Maildir maildir(maildirPath, false);
443
444 const auto messagePathParts = filePath.split("/");
445 if (messagePathParts.isEmpty()) {
446 Warning() << "No message path available: " << remoteId;
447 return KAsync::error<void>(1, "No message path available.");
448 }
449 const auto newIdentifier = messagePathParts.last();
450 QString identifier;
451 if (newIdentifier != KPIM::Maildir::getKeyFromFile(remoteId)) {
452 //Remove the old mime message if it changed
453 Trace() << "Removing old mime message: " << remoteId;
454 QFile(remoteId).remove();
455 identifier = newIdentifier;
456 } else {
457 //The identifier needs to contain the flags for changeEntryFlags to work
458 Q_ASSERT(!remoteId.split('/').isEmpty());
459 identifier = remoteId.split('/').last();
460 }
461 392
462 //get flags from 393 const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
463 KPIM::Maildir::Flags flags; 394 const auto maildirPath = KPIM::Maildir::getDirectoryFromFile(filePath);
464 if (!mail.getUnread()) { 395 KPIM::Maildir maildir(maildirPath, false);
465 flags |= KPIM::Maildir::Seen;
466 }
467 if (mail.getImportant()) {
468 flags |= KPIM::Maildir::Flagged;
469 }
470 396
471 const auto newRemoteId = maildir.changeEntryFlags(identifier, flags); 397 const auto messagePathParts = filePath.split("/");
472 updateRemoteId(ENTITY_TYPE_MAIL, uid, QString(maildirPath + "/cur/" + newRemoteId).toUtf8(), synchronizationTransaction); 398 if (messagePathParts.isEmpty()) {
399 Warning() << "No message path available: " << oldRemoteId;
400 return KAsync::error<QByteArray>(1, "No message path available.");
401 }
402 const auto newIdentifier = messagePathParts.last();
403 QString identifier;
404 if (newIdentifier != KPIM::Maildir::getKeyFromFile(oldRemoteId)) {
405 //Remove the old mime message if it changed
406 Trace() << "Removing old mime message: " << oldRemoteId;
407 QFile(oldRemoteId).remove();
408 identifier = newIdentifier;
473 } else { 409 } else {
474 Warning() << "Unkown operation" << operation; 410 //The identifier needs to contain the flags for changeEntryFlags to work
411 Q_ASSERT(!oldRemoteId.split('/').isEmpty());
412 identifier = oldRemoteId.split('/').last();
475 } 413 }
414
415 //get flags from
416 KPIM::Maildir::Flags flags;
417 if (!mail.getUnread()) {
418 flags |= KPIM::Maildir::Seen;
419 }
420 if (mail.getImportant()) {
421 flags |= KPIM::Maildir::Flagged;
422 }
423
424 const auto newRemoteId = maildir.changeEntryFlags(identifier, flags);
425 Warning() << "New remote id: " << QString(maildirPath + "/cur/" + newRemoteId);
426 return KAsync::start<QByteArray>([=]() -> QByteArray {
427 return QString(maildirPath + "/cur/" + newRemoteId).toUtf8();
428 });
476 } 429 }
477 return KAsync::null<void>(); 430 return KAsync::null<QByteArray>();
431}
432
433KAsync::Job<QByteArray> MaildirResource::replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId)
434{
435 if (operation == Sink::Operation_Creation) {
436 auto folderName = folder.getName();
437 //FIXME handle non toplevel folders
438 auto path = mMaildirPath + "/" + folderName;
439 Trace() << "Creating a new folder: " << path;
440 KPIM::Maildir maildir(path, false);
441 maildir.create();
442 return KAsync::start<QByteArray>([=]() -> QByteArray {
443 return path.toUtf8();
444 });
445 } else if (operation == Sink::Operation_Removal) {
446 const auto path = oldRemoteId;
447 Trace() << "Removing a folder: " << path;
448 KPIM::Maildir maildir(path, false);
449 maildir.remove();
450 return KAsync::null<QByteArray>();
451 } else if (operation == Sink::Operation_Modification) {
452 Warning() << "Folder modifications are not implemented";
453 return KAsync::start<QByteArray>([=]() -> QByteArray {
454 return oldRemoteId;
455 });
456 }
457 return KAsync::null<QByteArray>();
478} 458}
479 459
480void MaildirResource::removeFromDisk(const QByteArray &instanceIdentifier) 460void MaildirResource::removeFromDisk(const QByteArray &instanceIdentifier)
diff --git a/examples/maildirresource/maildirresource.h b/examples/maildirresource/maildirresource.h
index 33ba3ae..e58dc16 100644
--- a/examples/maildirresource/maildirresource.h
+++ b/examples/maildirresource/maildirresource.h
@@ -50,7 +50,8 @@ public:
50 KAsync::Job<void> inspect(int inspectionType, const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue) Q_DECL_OVERRIDE; 50 KAsync::Job<void> inspect(int inspectionType, const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue) Q_DECL_OVERRIDE;
51 static void removeFromDisk(const QByteArray &instanceIdentifier); 51 static void removeFromDisk(const QByteArray &instanceIdentifier);
52private: 52private:
53 KAsync::Job<void> replay(Sink::Storage &synchronizationStore, const QByteArray &type, const QByteArray &key, const QByteArray &value) Q_DECL_OVERRIDE; 53 KAsync::Job<QByteArray> replay(const Sink::ApplicationDomain::Mail &, Sink::Operation, const QByteArray &oldRemoteId) Q_DECL_OVERRIDE;
54 KAsync::Job<QByteArray> replay(const Sink::ApplicationDomain::Folder &, Sink::Operation, const QByteArray &oldRemoteId) Q_DECL_OVERRIDE;
54 55
55 void synchronizeFolders(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction); 56 void synchronizeFolders(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction);
56 void synchronizeMails(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &folder); 57 void synchronizeMails(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &folder);