1 /*
2     SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #pragma once
8 
9 #include "agentbase.h"
10 #include "collection.h"
11 #include "item.h"
12 #include "resourcebase.h"
13 
14 #include <QDBusMessage>
15 #include <QObject>
16 
17 namespace Akonadi
18 {
19 class RecursiveMover;
20 
21 /// @cond PRIVATE
22 
23 /**
24   @internal
25 
26   Manages synchronization and fetch requests for a resource.
27 
28   @todo Attach to the ResourceBase Monitor,
29 */
30 class ResourceScheduler : public QObject
31 {
32     Q_OBJECT
33 
34 public:
35     // If you change this enum, keep s_taskTypes in sync in resourcescheduler.cpp
36     enum TaskType {
37         Invalid,
38         SyncAll,
39         SyncCollectionTree,
40         SyncCollection,
41         SyncCollectionAttributes,
42         SyncTags,
43         FetchItem,
44         FetchItems,
45         ChangeReplay,
46         RecursiveMoveReplay,
47         DeleteResourceCollection,
48         InvalideCacheForCollection,
49         SyncAllDone,
50         SyncCollectionTreeDone,
51         SyncRelations,
52         Custom
53     };
54 
55     class Task
56     {
57         static qint64 latestSerial;
58 
59     public:
Task()60         Task()
61             : serial(++latestSerial)
62             , type(Invalid)
63         {
64         }
65         qint64 serial;
66         TaskType type;
67         Collection collection;
68         QVector<Item> items;
69         QSet<QByteArray> itemParts;
70         QList<QDBusMessage> dbusMsgs;
71         QObject *receiver = nullptr;
72         QByteArray methodName;
73         QVariant argument;
74 
75         void sendDBusReplies(const QString &errorMsg);
76 
77         bool operator==(const Task &other) const
78         {
79             return type == other.type && (collection == other.collection || (!collection.isValid() && !other.collection.isValid())) && items == other.items
80                 && itemParts == other.itemParts && receiver == other.receiver && methodName == other.methodName && argument == other.argument;
81         }
82     };
83 
84     explicit ResourceScheduler(QObject *parent = nullptr);
85 
86     /**
87       Schedules a full synchronization.
88     */
89     void scheduleFullSync();
90 
91     /**
92       Schedules a collection tree sync.
93     */
94     void scheduleCollectionTreeSync();
95 
96     /**
97       Schedules the synchronization of a single collection.
98       @param col The collection to synchronize.
99     */
100     void scheduleSync(const Collection &col);
101 
102     /**
103       Schedules synchronizing the attributes of a single collection.
104       @param collection The collection to synchronize attributes from.
105     */
106     void scheduleAttributesSync(const Collection &collection);
107 
108     void scheduleTagSync();
109     void scheduleRelationSync();
110 
111     /**
112       Schedules fetching of a single PIM item.
113 
114       This task is only ever used if the resource still uses the old deprecated
115       retrieveItem() (instead of retrieveItems(Item::List)) method. This task has
116       a special meaning to the scheduler and instead of replying to the DBus message
117       after the single @p item is retrieved, the items are accumulated until all
118       tasks from the same messages are fetched.
119 
120       @param items The items to fetch.
121       @param parts List of names of the parts of the item to fetch.
122       @param msg The associated D-Bus message.
123       @param parentId ID of the original ItemsFetch task that this task was created from.
124                       We can use this ID to group the tasks together
125     */
126     void scheduleItemFetch(const Item &item, const QSet<QByteArray> &parts, const QList<QDBusMessage> &msgs, const qint64 parentId);
127 
128     /**
129       Schedules batch-fetching of PIM items.
130       @param items The items to fetch.
131       @param parts List of names of the parts of the item to fetch.
132       @param msg The associated D-Bus message.
133     */
134     void scheduleItemsFetch(const Item::List &item, const QSet<QByteArray> &parts, const QDBusMessage &msg);
135 
136     /**
137       Schedules deletion of the resource collection.
138       This method is used to implement the ResourceBase::clearCache() functionality.
139      */
140     void scheduleResourceCollectionDeletion();
141 
142     /**
143      * Schedule cache invalidation for @p collection.
144      * @see ResourceBase::invalidateCache()
145      */
146     void scheduleCacheInvalidation(const Collection &collection);
147 
148     /**
149       Insert synchronization completion marker into the task queue.
150     */
151     void scheduleFullSyncCompletion();
152 
153     /**
154       Insert collection tree synchronization completion marker into the task queue.
155     */
156     void scheduleCollectionTreeSyncCompletion();
157 
158     /**
159       Insert a custom task.
160       @param methodName The method name, without signature, do not use the SLOT() macro
161     */
162     void
163     scheduleCustomTask(QObject *receiver, const char *methodName, const QVariant &argument, ResourceBase::SchedulePriority priority = ResourceBase::Append);
164 
165     /**
166      * Schedule a recursive move replay.
167      */
168     void scheduleMoveReplay(const Collection &movedCollection, RecursiveMover *mover);
169 
170     /**
171       Returns true if no tasks are running or in the queue.
172     */
173     bool isEmpty();
174 
175     /**
176       Returns the current task.
177     */
178     Task currentTask() const;
179 
180     Task &currentTask();
181 
182     /**
183       Sets the online state.
184     */
185     void setOnline(bool state);
186 
187     /**
188        Print debug output showing the state of the scheduler.
189     */
190     void dump() const;
191     /**
192        Print debug output showing the state of the scheduler.
193     */
194     QString dumpToString() const;
195 
196     /**
197        Clear the state of the scheduler. Warning: this is intended to be
198        used purely in debugging scenarios, as it might cause loss of uncommitted
199        local changes.
200     */
201     void clear();
202 
203     /**
204        Cancel everything the scheduler has still in queue. Keep the current task running though.
205        It can be seen as a less aggressive clear() used when the user requested the resource to
206        abort its activities. It properly cancel all the tasks in there.
207     */
208     void cancelQueues();
209 
210 public Q_SLOTS:
211     /**
212       Schedules replaying changes.
213     */
214     void scheduleChangeReplay();
215 
216     /**
217       The current task has been finished
218     */
219     void taskDone();
220 
221     /**
222       Like taskDone(), but special case for ItemFetch task
223     */
224     void itemFetchDone(const QString &msg);
225 
226     /**
227       The current task can't be finished now and will be rescheduled later
228     */
229     void deferTask();
230 
231     /**
232       Remove tasks that affect @p collection.
233     */
234     void collectionRemoved(const Akonadi::Collection &collection);
235 
236 Q_SIGNALS:
237     void executeFullSync();
238     void executeCollectionAttributesSync(const Akonadi::Collection &col);
239     void executeCollectionSync(const Akonadi::Collection &col);
240     void executeCollectionTreeSync();
241     void executeTagSync();
242     void executeRelationSync();
243     void executeItemFetch(const Akonadi::Item &item, const QSet<QByteArray> &parts);
244     void executeItemsFetch(const QVector<Akonadi::Item> &items, const QSet<QByteArray> &parts);
245     void executeResourceCollectionDeletion();
246     void executeCacheInvalidation(const Akonadi::Collection &collection);
247     void executeChangeReplay();
248     void executeRecursiveMoveReplay(RecursiveMover *mover);
249     void collectionTreeSyncComplete();
250     void fullSyncComplete();
251     void status(int status, const QString &message = QString());
252 
253 private Q_SLOTS:
254     void scheduleNext();
255     void executeNext();
256 
257 private:
258     void signalTaskToTracker(const Task &task, const QByteArray &taskType, const QString &debugString = QString());
259 
260     // We have a number of task queues, by order of priority.
261     // * PrependTaskQueue is for deferring the current task
262     // * ChangeReplay must be first:
263     //    change replays have to happen before we pull changes from the backend, otherwise
264     //    we will overwrite our still unsaved local changes if the backend can't do
265     //    incremental retrieval
266     //
267     // * then the stuff that is "immediately after change replay", like writeFile calls.
268     // * then tasks which the user is waiting for, like ItemFetch (clicking on a mail) or
269     //        SyncCollectionAttributes (folder properties dialog in kmail)
270     // * then everything else (which includes the background email checking, which can take quite some time).
271     enum QueueType {
272         PrependTaskQueue,
273         ChangeReplayQueue, // one task at most
274         AfterChangeReplayQueue, // also one task at most, currently
275         UserActionQueue,
276         GenericTaskQueue,
277         NQueueCount
278     };
279     using TaskList = QList<Task>;
280 
281     static QueueType queueTypeForTaskType(TaskType type);
282     TaskList &queueForTaskType(TaskType type);
283 
284     TaskList mTaskList[NQueueCount];
285 
286     Task mCurrentTask;
287     int mCurrentTasksQueue = -1; // queue mCurrentTask came from
288     bool mOnline = false;
289 };
290 
291 QDebug operator<<(QDebug, const ResourceScheduler::Task &task);
292 QTextStream &operator<<(QTextStream &, const ResourceScheduler::Task &task);
293 
294 /// @endcond
295 
296 }
297 
298