diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-12-09 16:31:30 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-12-09 16:31:30 +0100 |
commit | 9f6751df8e6f483b112c2b24c0bc725924f17356 (patch) | |
tree | df312279bacc53e5bb6b6181fc25282ce5b0dd8e | |
parent | 9c8e4612403fc2accc26ac2b49670394972f1293 (diff) | |
download | sink-9f6751df8e6f483b112c2b24c0bc725924f17356.tar.gz sink-9f6751df8e6f483b112c2b24c0bc725924f17356.zip |
Move the BLOB property handling to the entitystore.
This is really part of the storage, and will help us to cleanly
implement features like moving properties into a temporary place when
reading in a clean way as well.
-rw-r--r-- | common/domain/applicationdomaintype.h | 8 | ||||
-rw-r--r-- | common/mailpreprocessor.cpp | 46 | ||||
-rw-r--r-- | common/mailpreprocessor.h | 13 | ||||
-rw-r--r-- | common/storage/entitystore.cpp | 51 | ||||
-rw-r--r-- | common/storage/entitystore.h | 1 | ||||
-rw-r--r-- | examples/dummyresource/resourcefactory.cpp | 2 | ||||
-rw-r--r-- | examples/imapresource/imapresource.cpp | 2 | ||||
-rw-r--r-- | examples/maildirresource/maildirresource.cpp | 14 | ||||
-rw-r--r-- | examples/mailtransportresource/mailtransportresource.cpp | 2 |
9 files changed, 74 insertions, 65 deletions
diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h index 7c3800d..1848224 100644 --- a/common/domain/applicationdomaintype.h +++ b/common/domain/applicationdomaintype.h | |||
@@ -99,7 +99,15 @@ struct SINK_EXPORT Progress { | |||
99 | }; | 99 | }; |
100 | 100 | ||
101 | struct BLOB { | 101 | struct BLOB { |
102 | BLOB() = default; | ||
103 | BLOB(const BLOB &) = default; | ||
104 | BLOB(const QString &id) : value(id) {}; | ||
105 | ~BLOB() = default; | ||
106 | bool operator==(const BLOB &other) const { | ||
107 | return value == other.value && isExternal == other.isExternal; | ||
108 | } | ||
102 | QString value; | 109 | QString value; |
110 | bool isExternal = true; | ||
103 | }; | 111 | }; |
104 | 112 | ||
105 | /** | 113 | /** |
diff --git a/common/mailpreprocessor.cpp b/common/mailpreprocessor.cpp index 575ff4b..bde8a64 100644 --- a/common/mailpreprocessor.cpp +++ b/common/mailpreprocessor.cpp | |||
@@ -152,49 +152,3 @@ void MailPropertyExtractor::modifiedEntity(const Sink::ApplicationDomain::Mail & | |||
152 | } | 152 | } |
153 | } | 153 | } |
154 | 154 | ||
155 | |||
156 | MimeMessageMover::MimeMessageMover() : Sink::EntityPreprocessor<ApplicationDomain::Mail>() | ||
157 | { | ||
158 | } | ||
159 | |||
160 | QString MimeMessageMover::moveMessage(const QString &oldPath, const Sink::ApplicationDomain::Mail &mail) | ||
161 | { | ||
162 | const auto directory = Sink::resourceStorageLocation(resourceInstanceIdentifier()); | ||
163 | const auto filePath = directory + "/" + mail.identifier(); | ||
164 | if (oldPath != filePath) { | ||
165 | if (!QDir().mkpath(directory)) { | ||
166 | SinkWarning() << "Failed to create the directory: " << directory; | ||
167 | } | ||
168 | QFile::remove(filePath); | ||
169 | QFile origFile(oldPath); | ||
170 | if (!origFile.open(QIODevice::ReadWrite)) { | ||
171 | SinkWarning() << "Failed to open the original file with write rights: " << origFile.errorString(); | ||
172 | } | ||
173 | if (!origFile.rename(filePath)) { | ||
174 | SinkWarning() << "Failed to move the file from: " << oldPath << " to " << filePath << ". " << origFile.errorString(); | ||
175 | } | ||
176 | origFile.close(); | ||
177 | return filePath; | ||
178 | } | ||
179 | return oldPath; | ||
180 | } | ||
181 | |||
182 | void MimeMessageMover::newEntity(Sink::ApplicationDomain::Mail &mail) | ||
183 | { | ||
184 | if (!mail.getMimeMessagePath().isEmpty()) { | ||
185 | mail.setMimeMessagePath(moveMessage(mail.getMimeMessagePath(), mail)); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | void MimeMessageMover::modifiedEntity(const Sink::ApplicationDomain::Mail &oldMail, Sink::ApplicationDomain::Mail &newMail) | ||
190 | { | ||
191 | if (!newMail.getMimeMessagePath().isEmpty()) { | ||
192 | newMail.setMimeMessagePath(moveMessage(newMail.getMimeMessagePath(), newMail)); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | void MimeMessageMover::deletedEntity(const Sink::ApplicationDomain::Mail &mail) | ||
197 | { | ||
198 | QFile::remove(mail.getMimeMessagePath()); | ||
199 | } | ||
200 | |||
diff --git a/common/mailpreprocessor.h b/common/mailpreprocessor.h index f979f22..a24a8d3 100644 --- a/common/mailpreprocessor.h +++ b/common/mailpreprocessor.h | |||
@@ -30,16 +30,3 @@ protected: | |||
30 | virtual QString getFilePathFromMimeMessagePath(const QString &) const; | 30 | virtual QString getFilePathFromMimeMessagePath(const QString &) const; |
31 | }; | 31 | }; |
32 | 32 | ||
33 | class SINK_EXPORT MimeMessageMover : public Sink::EntityPreprocessor<Sink::ApplicationDomain::Mail> | ||
34 | { | ||
35 | public: | ||
36 | MimeMessageMover(); | ||
37 | virtual ~MimeMessageMover(){} | ||
38 | |||
39 | void newEntity(Sink::ApplicationDomain::Mail &mail) Q_DECL_OVERRIDE; | ||
40 | void modifiedEntity(const Sink::ApplicationDomain::Mail &oldMail, Sink::ApplicationDomain::Mail &newMail) Q_DECL_OVERRIDE; | ||
41 | void deletedEntity(const Sink::ApplicationDomain::Mail &mail) Q_DECL_OVERRIDE; | ||
42 | |||
43 | private: | ||
44 | QString moveMessage(const QString &oldPath, const Sink::ApplicationDomain::Mail &mail); | ||
45 | }; | ||
diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp index 999bb2c..d8b1121 100644 --- a/common/storage/entitystore.cpp +++ b/common/storage/entitystore.cpp | |||
@@ -19,6 +19,9 @@ | |||
19 | */ | 19 | */ |
20 | #include "entitystore.h" | 20 | #include "entitystore.h" |
21 | 21 | ||
22 | #include <QDir> | ||
23 | #include <QFile> | ||
24 | |||
22 | #include "entitybuffer.h" | 25 | #include "entitybuffer.h" |
23 | #include "log.h" | 26 | #include "log.h" |
24 | #include "typeindex.h" | 27 | #include "typeindex.h" |
@@ -90,6 +93,11 @@ public: | |||
90 | return ApplicationDomain::ApplicationDomainType{resourceContext.instanceId(), uid, revision, adaptor}; | 93 | return ApplicationDomain::ApplicationDomainType{resourceContext.instanceId(), uid, revision, adaptor}; |
91 | } | 94 | } |
92 | 95 | ||
96 | QString entityBlobStoragePath(const QByteArray &id) | ||
97 | { | ||
98 | return Sink::resourceStorageLocation(resourceContext.instanceId()) + "/blob/" + id; | ||
99 | } | ||
100 | |||
93 | }; | 101 | }; |
94 | 102 | ||
95 | EntityStore::EntityStore(const ResourceContext &context) | 103 | EntityStore::EntityStore(const ResourceContext &context) |
@@ -120,6 +128,38 @@ void EntityStore::abortTransaction() | |||
120 | d->transaction = Storage::DataStore::Transaction(); | 128 | d->transaction = Storage::DataStore::Transaction(); |
121 | } | 129 | } |
122 | 130 | ||
131 | void EntityStore::copyBlobs(ApplicationDomain::ApplicationDomainType &entity, qint64 newRevision) | ||
132 | { | ||
133 | const auto directory = d->entityBlobStoragePath(entity.identifier()); | ||
134 | if (!QDir().mkpath(directory)) { | ||
135 | SinkWarning() << "Failed to create the directory: " << directory; | ||
136 | } | ||
137 | |||
138 | for (const auto &property : entity.changedProperties()) { | ||
139 | const auto value = entity.getProperty(property); | ||
140 | if (value.canConvert<ApplicationDomain::BLOB>()) { | ||
141 | const auto blob = value.value<ApplicationDomain::BLOB>(); | ||
142 | bool blobIsExternal = blob.isExternal; | ||
143 | //Any blob that is not part of the storage yet has to be moved there. | ||
144 | if (blob.isExternal) { | ||
145 | auto oldPath = blob.value; | ||
146 | auto filePath = directory + QString("/%1%2.blob").arg(QString::number(newRevision)).arg(QString::fromLatin1(property)); | ||
147 | //In case we hit the same revision again due to a rollback. | ||
148 | QFile::remove(filePath); | ||
149 | QFile origFile(oldPath); | ||
150 | if (!origFile.open(QIODevice::ReadWrite)) { | ||
151 | SinkWarning() << "Failed to open the original file with write rights: " << origFile.errorString(); | ||
152 | } | ||
153 | if (!origFile.rename(filePath)) { | ||
154 | SinkWarning() << "Failed to move the file from: " << oldPath << " to " << filePath << ". " << origFile.errorString(); | ||
155 | } | ||
156 | origFile.close(); | ||
157 | entity.setProperty(property, QVariant::fromValue(ApplicationDomain::BLOB{filePath})); | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | |||
123 | bool EntityStore::add(const QByteArray &type, const ApplicationDomain::ApplicationDomainType &entity_, bool replayToSource, const PreprocessCreation &preprocess) | 163 | bool EntityStore::add(const QByteArray &type, const ApplicationDomain::ApplicationDomainType &entity_, bool replayToSource, const PreprocessCreation &preprocess) |
124 | { | 164 | { |
125 | if (entity_.identifier().isEmpty()) { | 165 | if (entity_.identifier().isEmpty()) { |
@@ -138,6 +178,8 @@ bool EntityStore::add(const QByteArray &type, const ApplicationDomain::Applicati | |||
138 | //The maxRevision may have changed meanwhile if the entity created sub-entities | 178 | //The maxRevision may have changed meanwhile if the entity created sub-entities |
139 | const qint64 newRevision = maxRevision() + 1; | 179 | const qint64 newRevision = maxRevision() + 1; |
140 | 180 | ||
181 | copyBlobs(entity, newRevision); | ||
182 | |||
141 | // Add metadata buffer | 183 | // Add metadata buffer |
142 | flatbuffers::FlatBufferBuilder metadataFbb; | 184 | flatbuffers::FlatBufferBuilder metadataFbb; |
143 | auto metadataBuilder = MetadataBuilder(metadataFbb); | 185 | auto metadataBuilder = MetadataBuilder(metadataFbb); |
@@ -192,6 +234,8 @@ bool EntityStore::modify(const QByteArray &type, const ApplicationDomain::Applic | |||
192 | 234 | ||
193 | const qint64 newRevision = DataStore::maxRevision(d->transaction) + 1; | 235 | const qint64 newRevision = DataStore::maxRevision(d->transaction) + 1; |
194 | 236 | ||
237 | copyBlobs(newEntity, newRevision); | ||
238 | |||
195 | // Add metadata buffer | 239 | // Add metadata buffer |
196 | flatbuffers::FlatBufferBuilder metadataFbb; | 240 | flatbuffers::FlatBufferBuilder metadataFbb; |
197 | { | 241 | { |
@@ -297,6 +341,13 @@ void EntityStore::cleanupRevision(qint64 revision) | |||
297 | DataStore::removeRevision(d->transaction, rev); | 341 | DataStore::removeRevision(d->transaction, rev); |
298 | DataStore::mainDatabase(d->transaction, bufferType).remove(key); | 342 | DataStore::mainDatabase(d->transaction, bufferType).remove(key); |
299 | } | 343 | } |
344 | if (metadata->operation() == Operation_Removal) { | ||
345 | const auto directory = d->entityBlobStoragePath(uid); | ||
346 | QDir dir(directory); | ||
347 | if (!dir.removeRecursively()) { | ||
348 | SinkError() << "Failed to cleanup: " << directory; | ||
349 | } | ||
350 | } | ||
300 | } | 351 | } |
301 | 352 | ||
302 | return true; | 353 | return true; |
diff --git a/common/storage/entitystore.h b/common/storage/entitystore.h index 06ca8c4..c626ebc 100644 --- a/common/storage/entitystore.h +++ b/common/storage/entitystore.h | |||
@@ -110,6 +110,7 @@ public: | |||
110 | qint64 maxRevision(); | 110 | qint64 maxRevision(); |
111 | 111 | ||
112 | private: | 112 | private: |
113 | void copyBlobs(ApplicationDomain::ApplicationDomainType &entity, qint64 newRevision); | ||
113 | class Private; | 114 | class Private; |
114 | const QSharedPointer<Private> d; | 115 | const QSharedPointer<Private> d; |
115 | }; | 116 | }; |
diff --git a/examples/dummyresource/resourcefactory.cpp b/examples/dummyresource/resourcefactory.cpp index 07021b9..03238ef 100644 --- a/examples/dummyresource/resourcefactory.cpp +++ b/examples/dummyresource/resourcefactory.cpp | |||
@@ -163,7 +163,7 @@ DummyResource::DummyResource(const Sink::ResourceContext &resourceContext, const | |||
163 | setupSynchronizer(QSharedPointer<DummySynchronizer>::create(resourceContext)); | 163 | setupSynchronizer(QSharedPointer<DummySynchronizer>::create(resourceContext)); |
164 | setupInspector(QSharedPointer<DummyInspector>::create(resourceContext)); | 164 | setupInspector(QSharedPointer<DummyInspector>::create(resourceContext)); |
165 | setupPreprocessors(ENTITY_TYPE_MAIL, | 165 | setupPreprocessors(ENTITY_TYPE_MAIL, |
166 | QVector<Sink::Preprocessor*>() << new MailPropertyExtractor << new MimeMessageMover << new SpecialPurposeProcessor{resourceContext.resourceType, resourceContext.instanceId()}); | 166 | QVector<Sink::Preprocessor*>() << new MailPropertyExtractor << new SpecialPurposeProcessor{resourceContext.resourceType, resourceContext.instanceId()}); |
167 | setupPreprocessors(ENTITY_TYPE_FOLDER, | 167 | setupPreprocessors(ENTITY_TYPE_FOLDER, |
168 | QVector<Sink::Preprocessor*>()); | 168 | QVector<Sink::Preprocessor*>()); |
169 | setupPreprocessors(ENTITY_TYPE_EVENT, | 169 | setupPreprocessors(ENTITY_TYPE_EVENT, |
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 238fb0c..252b910 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp | |||
@@ -737,7 +737,7 @@ ImapResource::ImapResource(const ResourceContext &resourceContext) | |||
737 | inspector->mPassword = password; | 737 | inspector->mPassword = password; |
738 | setupInspector(inspector); | 738 | setupInspector(inspector); |
739 | 739 | ||
740 | setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new SpecialPurposeProcessor(resourceContext.resourceType, resourceContext.instanceId()) << new MimeMessageMover << new MailPropertyExtractor); | 740 | setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new SpecialPurposeProcessor(resourceContext.resourceType, resourceContext.instanceId()) << new MailPropertyExtractor); |
741 | setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>()); | 741 | setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>()); |
742 | } | 742 | } |
743 | 743 | ||
diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp index a05afc6..6a03263 100644 --- a/examples/maildirresource/maildirresource.cpp +++ b/examples/maildirresource/maildirresource.cpp | |||
@@ -132,7 +132,10 @@ public: | |||
132 | auto mail = newEntity.cast<ApplicationDomain::Mail>(); | 132 | auto mail = newEntity.cast<ApplicationDomain::Mail>(); |
133 | const auto mimeMessage = mail.getMimeMessagePath(); | 133 | const auto mimeMessage = mail.getMimeMessagePath(); |
134 | if (!mimeMessage.isNull()) { | 134 | if (!mimeMessage.isNull()) { |
135 | mail.setMimeMessagePath(moveMessage(mimeMessage, mail.getFolder())); | 135 | const auto path = moveMessage(mimeMessage, mail.getFolder()); |
136 | auto blob = ApplicationDomain::BLOB{path}; | ||
137 | blob.isExternal = false; | ||
138 | mail.setProperty(ApplicationDomain::Mail::MimeMessage::name, QVariant::fromValue(blob)); | ||
136 | } | 139 | } |
137 | } | 140 | } |
138 | 141 | ||
@@ -149,7 +152,9 @@ public: | |||
149 | auto newPath = moveMessage(mimeMessage, newMail.getFolder()); | 152 | auto newPath = moveMessage(mimeMessage, newMail.getFolder()); |
150 | if (newPath != oldMail.getMimeMessagePath()) { | 153 | if (newPath != oldMail.getMimeMessagePath()) { |
151 | const auto oldPath = getFilePathFromMimeMessagePath(oldMail.getMimeMessagePath()); | 154 | const auto oldPath = getFilePathFromMimeMessagePath(oldMail.getMimeMessagePath()); |
152 | newMail.setMimeMessagePath(newPath); | 155 | auto blob = ApplicationDomain::BLOB{newPath}; |
156 | blob.isExternal = false; | ||
157 | newMail.setProperty(ApplicationDomain::Mail::MimeMessage::name, QVariant::fromValue(blob)); | ||
153 | //Remove the olde mime message if there is a new one | 158 | //Remove the olde mime message if there is a new one |
154 | QFile::remove(oldPath); | 159 | QFile::remove(oldPath); |
155 | } | 160 | } |
@@ -323,7 +328,10 @@ public: | |||
323 | Sink::ApplicationDomain::Mail mail; | 328 | Sink::ApplicationDomain::Mail mail; |
324 | mail.setFolder(folderLocalId); | 329 | mail.setFolder(folderLocalId); |
325 | //We only store the directory path + key, so we facade can add the changing bits (flags) | 330 | //We only store the directory path + key, so we facade can add the changing bits (flags) |
326 | mail.setMimeMessagePath(KPIM::Maildir::getDirectoryFromFile(filePath) + maildirKey); | 331 | auto path = KPIM::Maildir::getDirectoryFromFile(filePath) + maildirKey; |
332 | auto blob = ApplicationDomain::BLOB{path}; | ||
333 | blob.isExternal = false; | ||
334 | mail.setProperty(ApplicationDomain::Mail::MimeMessage::name, QVariant::fromValue(blob)); | ||
327 | mail.setUnread(!flags.testFlag(KPIM::Maildir::Seen)); | 335 | mail.setUnread(!flags.testFlag(KPIM::Maildir::Seen)); |
328 | mail.setImportant(flags.testFlag(KPIM::Maildir::Flagged)); | 336 | mail.setImportant(flags.testFlag(KPIM::Maildir::Flagged)); |
329 | 337 | ||
diff --git a/examples/mailtransportresource/mailtransportresource.cpp b/examples/mailtransportresource/mailtransportresource.cpp index 08a8748..b7ee77a 100644 --- a/examples/mailtransportresource/mailtransportresource.cpp +++ b/examples/mailtransportresource/mailtransportresource.cpp | |||
@@ -182,7 +182,7 @@ MailtransportResource::MailtransportResource(const Sink::ResourceContext &resour | |||
182 | setupSynchronizer(synchronizer); | 182 | setupSynchronizer(synchronizer); |
183 | setupInspector(QSharedPointer<MailtransportInspector>::create(resourceContext)); | 183 | setupInspector(QSharedPointer<MailtransportInspector>::create(resourceContext)); |
184 | 184 | ||
185 | setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new MimeMessageMover << new MailPropertyExtractor); | 185 | setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new MailPropertyExtractor); |
186 | } | 186 | } |
187 | 187 | ||
188 | MailtransportResourceFactory::MailtransportResourceFactory(QObject *parent) | 188 | MailtransportResourceFactory::MailtransportResourceFactory(QObject *parent) |