1 | /* |
2 | Copyright (c) 2007 Tobias Koenig <tokoe@kde.org> |
3 | |
4 | This library is free software; you can redistribute it and/or modify it |
5 | under the terms of the GNU Library General Public License as published by |
6 | the Free Software Foundation; either version 2 of the License, or (at your |
7 | option) any later version. |
8 | |
9 | This library is distributed in the hope that it will be useful, but WITHOUT |
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public |
12 | License for more details. |
13 | |
14 | You should have received a copy of the GNU Library General Public License |
15 | along with this library; see the file COPYING.LIB. If not, write to the |
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
17 | 02110-1301, USA. |
18 | */ |
19 | |
20 | #ifndef AKONADI_MONITOR_P_H |
21 | #define AKONADI_MONITOR_P_H |
22 | |
23 | #include "akonadiprivate_export.h" |
24 | #include "monitor.h" |
25 | #include "collection.h" |
26 | #include "collectionstatisticsjob.h" |
27 | #include "collectionfetchscope.h" |
28 | #include "item.h" |
29 | #include "itemfetchscope.h" |
30 | #include "tagfetchscope.h" |
31 | #include "job.h" |
32 | #include "entitycache_p.h" |
33 | #include "servermanager.h" |
34 | #include "changenotificationdependenciesfactory_p.h" |
35 | #include "notificationsource_p.h" |
36 | |
37 | #include <akonadi/private/notificationmessagev3_p.h> |
38 | |
39 | #include <kmimetype.h> |
40 | |
41 | #include <QtCore/QObject> |
42 | #include <QtCore/QTimer> |
43 | |
44 | namespace Akonadi { |
45 | |
46 | class Monitor; |
47 | |
48 | /** |
49 | * @internal |
50 | */ |
51 | class AKONADI_TESTS_EXPORT MonitorPrivate |
52 | { |
53 | public: |
54 | MonitorPrivate(ChangeNotificationDependenciesFactory *dependenciesFactory_, Monitor *parent); |
55 | virtual ~MonitorPrivate() { |
56 | delete dependenciesFactory; |
57 | delete collectionCache; |
58 | delete itemCache; |
59 | } |
60 | void init(); |
61 | |
62 | Monitor *q_ptr; |
63 | Q_DECLARE_PUBLIC(Monitor) |
64 | ChangeNotificationDependenciesFactory *dependenciesFactory; |
65 | NotificationSource *notificationSource; |
66 | Collection::List collections; |
67 | QSet<QByteArray> resources; |
68 | QSet<Item::Id> items; |
69 | QSet<Tag::Id> tags; |
70 | QSet<Monitor::Type> types; |
71 | QSet<QString> mimetypes; |
72 | bool monitorAll; |
73 | QList<QByteArray> sessions; |
74 | ItemFetchScope mItemFetchScope; |
75 | TagFetchScope mTagFetchScope; |
76 | CollectionFetchScope mCollectionFetchScope; |
77 | bool mFetchChangedOnly; |
78 | Session *session; |
79 | CollectionCache *collectionCache; |
80 | ItemListCache *itemCache; |
81 | TagListCache *tagCache; |
82 | |
83 | // The waiting list |
84 | QQueue<NotificationMessageV3> pendingNotifications; |
85 | // The messages for which data is currently being fetched |
86 | QQueue<NotificationMessageV3> pipeline; |
87 | // In a pure Monitor, the pipeline contains items that were dequeued from pendingNotifications. |
88 | // The ordering [pipeline] [pendingNotifications] is kept at all times. |
89 | // [] [A B C] -> [A B] [C] -> [B] [C] -> [B C] [] -> [C] [] -> [] |
90 | // In a ChangeRecorder, the pipeline contains one item only, and not dequeued yet. |
91 | // [] [A B C] -> [A] [A B C] -> [] [A B C] -> (changeProcessed) [] [B C] -> [B] [B C] etc... |
92 | |
93 | bool fetchCollection; |
94 | bool fetchCollectionStatistics; |
95 | bool collectionMoveTranslationEnabled; |
96 | |
97 | // Virtual methods for ChangeRecorder |
98 | virtual void notificationsEnqueued(int) |
99 | { |
100 | } |
101 | virtual void notificationsErased() |
102 | { |
103 | } |
104 | |
105 | // Virtual so it can be overridden in FakeMonitor. |
106 | virtual bool connectToNotificationManager(); |
107 | bool acceptNotification(const NotificationMessageV3 &msg) const; |
108 | void dispatchNotifications(); |
109 | void flushPipeline(); |
110 | |
111 | // Called when the monitored item/collection changes, checks if the queued messages |
112 | // are still accepted, if not they are removed |
113 | void cleanOldNotifications(); |
114 | |
115 | bool ensureDataAvailable(const NotificationMessageV3 &msg); |
116 | /** |
117 | * Sends out the change notification @p msg. |
118 | * @param msg the change notification to send |
119 | * @return @c true if the notification was actually send to someone, @c false if no one was listening. |
120 | */ |
121 | virtual bool emitNotification(const NotificationMessageV3 &msg); |
122 | void updatePendingStatistics(const NotificationMessageV3 &msg); |
123 | void invalidateCaches(const NotificationMessageV3 &msg); |
124 | |
125 | /** Used by ResourceBase to inform us about collection changes before the notifications are emitted, |
126 | needed to avoid the missing RID race on change replay. |
127 | */ |
128 | void invalidateCache(const Collection &col); |
129 | |
130 | /// Virtual so that ChangeRecorder can set it to 0 and handle the pipeline itself |
131 | virtual int pipelineSize() const; |
132 | |
133 | // private Q_SLOTS |
134 | void dataAvailable(); |
135 | void slotSessionDestroyed(QObject *object); |
136 | void slotStatisticsChangedFinished(KJob *job); |
137 | void slotFlushRecentlyChangedCollections(); |
138 | |
139 | /** |
140 | Returns whether a message was appended to @p notificationQueue |
141 | */ |
142 | int translateAndCompress(QQueue<NotificationMessageV3> ¬ificationQueue, const NotificationMessageV3 &msg); |
143 | |
144 | virtual void slotNotify(const NotificationMessageV3::List &msgs); |
145 | |
146 | /** |
147 | * Sends out a change notification for an item. |
148 | * @return @c true if the notification was actually send to someone, @c false if no one was listening. |
149 | */ |
150 | bool emitItemsNotification(const NotificationMessageV3 &msg, const Item::List &items = Item::List(), |
151 | const Collection &collection = Collection(), const Collection &collectionDest = Collection()); |
152 | /** |
153 | * Sends out a change notification for a collection. |
154 | * @return @c true if the notification was actually send to someone, @c false if no one was listening. |
155 | */ |
156 | bool emitCollectionNotification(const NotificationMessageV3 &msg, const Collection &col = Collection(), |
157 | const Collection &par = Collection(), const Collection &dest = Collection()); |
158 | |
159 | bool emitTagsNotification(const NotificationMessageV3 &msg, const Tag::List &tags); |
160 | |
161 | void serverStateChanged(Akonadi::ServerManager::State state); |
162 | |
163 | /** |
164 | * This method is called by the ChangeMediator to enforce an invalidation of the passed collection. |
165 | */ |
166 | void invalidateCollectionCache(qint64 collectionId); |
167 | |
168 | /** |
169 | * This method is called by the ChangeMediator to enforce an invalidation of the passed item. |
170 | */ |
171 | void invalidateItemCache(qint64 itemId); |
172 | |
173 | /** |
174 | * This method is called by the ChangeMediator to enforce an invalidation of the passed tag. |
175 | */ |
176 | void invalidateTagCache(qint64 tagId); |
177 | |
178 | /** |
179 | @brief Class used to determine when to purge items in a Collection |
180 | |
181 | The buffer method can be used to buffer a Collection. This may cause another Collection |
182 | to be purged if it is removed from the buffer. |
183 | |
184 | The purge method is used to purge a Collection from the buffer, but not the model. |
185 | This is used for example, to not buffer Collections anymore if they get referenced, |
186 | and to ensure that one Collection does not appear twice in the buffer. |
187 | |
188 | Check whether a Collection is buffered using the isBuffered method. |
189 | */ |
190 | class AKONADI_TESTS_EXPORT PurgeBuffer |
191 | { |
192 | // Buffer the most recent 10 unreferenced Collections |
193 | static const int MAXBUFFERSIZE = 10; |
194 | public: |
195 | explicit PurgeBuffer() |
196 | { |
197 | } |
198 | |
199 | /** |
200 | Adds @p id to the Collections to be buffered |
201 | |
202 | @returns The collection id which was removed form the buffer or -1 if none. |
203 | */ |
204 | Collection::Id buffer(Collection::Id id); |
205 | |
206 | /** |
207 | Removes @p id from the Collections being buffered |
208 | */ |
209 | void purge(Collection::Id id); |
210 | |
211 | bool isBuffered(Collection::Id id) const |
212 | { |
213 | return m_buffer.contains(id); |
214 | } |
215 | |
216 | static int buffersize(); |
217 | |
218 | private: |
219 | QQueue<Collection::Id> m_buffer; |
220 | } m_buffer; |
221 | |
222 | QHash<Collection::Id, int> refCountMap; |
223 | bool useRefCounting; |
224 | void ref(Collection::Id id); |
225 | Collection::Id deref(Collection::Id id); |
226 | |
227 | /** |
228 | * Returns true if the collection is monitored by monitor. |
229 | * |
230 | * A collection is always monitored if useRefCounting is false. |
231 | * If ref counting is used, the collection is only monitored, |
232 | * if the collection is either in refCountMap or m_buffer. |
233 | * If ref counting is used and the collection is not in refCountMap or m_buffer, |
234 | * no updates for the contained items are emitted, because they are lazily ignored. |
235 | */ |
236 | bool isMonitored(Collection::Id colId) const; |
237 | |
238 | private: |
239 | // collections that need a statistics update |
240 | QSet<Collection::Id> recentlyChangedCollections; |
241 | QTimer statisticsCompressionTimer; |
242 | |
243 | /** |
244 | @returns True if @p msg should be ignored. Otherwise appropriate signals are emitted for it. |
245 | */ |
246 | bool isLazilyIgnored(const NotificationMessageV3 &msg, bool allowModifyFlagsConversion = false) const; |
247 | |
248 | /** |
249 | Sets @p needsSplit to True when @p msg contains more than one item and there's at least one |
250 | listener that does not support batch operations. Sets @p batchSupported to True when |
251 | there's at least one listener that supports batch operations. |
252 | */ |
253 | void checkBatchSupport(const NotificationMessageV3 &msg, bool &needsSplit, bool &batchSupported) const; |
254 | |
255 | NotificationMessageV3::List splitMessage(const NotificationMessageV3 &msg, bool legacy) const; |
256 | |
257 | bool isCollectionMonitored(Collection::Id collection) const |
258 | { |
259 | if (collection < 0) { |
260 | return false; |
261 | } |
262 | if (collections.contains(Collection(collection))) { |
263 | return true; |
264 | } |
265 | if (collections.contains(Collection::root())) { |
266 | return true; |
267 | } |
268 | return false; |
269 | } |
270 | |
271 | bool isMimeTypeMonitored(const QString &mimetype) const |
272 | { |
273 | if (mimetypes.contains(mimetype)) { |
274 | return true; |
275 | } |
276 | |
277 | KMimeType::Ptr mimeType = KMimeType::mimeType(mimetype, KMimeType::ResolveAliases); |
278 | if (mimeType.isNull()) { |
279 | return false; |
280 | } |
281 | |
282 | foreach (const QString &mt, mimetypes) { |
283 | if (mimeType->is(mt)) { |
284 | return true; |
285 | } |
286 | } |
287 | |
288 | return false; |
289 | } |
290 | |
291 | bool isMoveDestinationResourceMonitored(const NotificationMessageV3 &msg) const |
292 | { |
293 | if (msg.operation() != NotificationMessageV2::Move) { |
294 | return false; |
295 | } |
296 | return resources.contains(msg.destinationResource()); |
297 | } |
298 | |
299 | void fetchStatistics(Collection::Id colId) |
300 | { |
301 | CollectionStatisticsJob *job = new CollectionStatisticsJob(Collection(colId), session); |
302 | QObject::connect(job, SIGNAL(result(KJob*)), q_ptr, SLOT(slotStatisticsChangedFinished(KJob*))); |
303 | } |
304 | |
305 | void notifyCollectionStatisticsWatchers(Collection::Id collection, const QByteArray &resource); |
306 | }; |
307 | |
308 | } |
309 | |
310 | #endif |
311 | |