2 This file is part of Akonadi.
4 Copyright (c) 2009 KDAB
5 Author: Till Adam <adam@kde.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
23 #include "jobtracker.h"
24 #include "jobtrackeradaptor.h"
25 #include "akonadiconsole_debug.h"
27 #include <QStringList>
33 QString
JobInfo::stateAsString() const
37 return QStringLiteral("Waiting");
39 return QStringLiteral("Running");
41 return QStringLiteral("Ended");
43 return QStringLiteral("Failed: %1").arg(error
);
45 return QStringLiteral("Unknown state!");
49 class JobTracker::Private
52 Private(JobTracker
*_q
)
53 : lastId(42), timer(_q
), disabled(false), q(_q
)
55 timer
.setSingleShot(true);
56 timer
.setInterval(200);
57 connect(&timer
, &QTimer::timeout
, q
, &JobTracker::signalUpdates
);
60 bool isSession(int id
) const
67 if (!timer
.isActive() && !disabled
) {
73 QHash
<QString
, int> idToSequence
;
74 QHash
<int, QString
> sequenceToId
;
75 QHash
<QString
, QStringList
> jobs
;
76 QHash
<QString
, JobInfo
> infoList
;
80 QList
< QPair
<int, int> > unpublishedAdds
;
81 QList
< QPair
<int, int> > unpublishedUpdates
;
86 JobTracker::JobTracker(const char *name
, QObject
*parent
)
87 : QObject(parent
), d(new Private(this))
89 new JobTrackerAdaptor(this);
90 QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.akonadiconsole"));
91 QDBusConnection::sessionBus().registerObject(QLatin1Char('/') + QLatin1String(name
),
92 this, QDBusConnection::ExportAdaptors
);
95 // dummy data for testing
96 d
->sessions
<< "one" << "two" << "three";
97 d
->jobs
.insert("one", QStringList() << "eins");
98 d
->jobs
.insert("two", QStringList());
99 d
->jobs
.insert("three", QStringList());
101 // create some fake jobs
102 d
->jobs
.insert("eins", QStringList() << "sub-eins" << "sub-zwei");
103 d
->idToSequence
.insert("eins", 0);
104 d
->sequenceToId
.insert(0, "eins");
108 d
->infoList
.insert("eins", info
);
110 d
->jobs
.insert("sub-eins", QStringList());
111 d
->idToSequence
.insert("sub-eins", 1);
112 d
->sequenceToId
.insert(1, "sub-eins");
113 info
.id
= "sub-eins";
115 d
->infoList
.insert("sub-eins", info
);
117 d
->jobs
.insert("sub-zwei", QStringList());
118 d
->idToSequence
.insert("sub-zwei", 2);
119 d
->sequenceToId
.insert(2, "sub-zwei");
120 info
.id
= "sub-zwei";
122 d
->infoList
.insert("sub-zwei", info
);
126 JobTracker::~JobTracker()
131 void JobTracker::jobCreated(const QString
&session
, const QString
&job
, const QString
&parent
, const QString
&jobType
, const QString
&debugString
)
133 if (d
->disabled
|| session
.isEmpty() || job
.isEmpty()) {
137 if (!parent
.isEmpty() && !d
->jobs
.contains(parent
)) {
138 qCWarning(AKONADICONSOLE_LOG
) << "JobTracker: Job arrived before its parent! Fix the library!";
139 jobCreated(session
, parent
, QString(), QStringLiteral("dummy job type"), QString());
141 // check if it's a new session, if so, add it
142 if (d
->sessions
.isEmpty() || !d
->sessions
.contains(session
)) {
143 d
->sessions
.append(session
);
144 d
->jobs
.insert(session
, QStringList());
145 d
->unpublishedAdds
<< QPair
<int, int>(d
->sessions
.count() - 1, -1);
149 if (d
->jobs
.contains(job
)) {
150 if (d
->infoList
.value(job
).state
== JobInfo::Running
) {
151 qCDebug(AKONADICONSOLE_LOG
) << "Job was already known and still running:" << job
<< "from" << d
->infoList
.value(job
).timestamp
.secsTo(QDateTime::currentDateTime()) << "s ago";
153 // otherwise it just means the pointer got reused... replace old job
156 d
->jobs
.insert(job
, QStringList());
160 if (parent
.isEmpty()) {
161 info
.parent
= idForSession(session
);
163 info
.parent
= idForJob(parent
);
165 info
.state
= JobInfo::Initial
;
166 info
.timestamp
= QDateTime::currentDateTime();
168 info
.debugString
= debugString
;
169 d
->infoList
.insert(job
, info
);
170 const int id
= d
->lastId
++;
171 d
->idToSequence
.insert(job
, id
);
172 d
->sequenceToId
.insert(id
, job
);
175 if (parent
.isEmpty()) {
181 assert(!daddy
.isEmpty());
182 QStringList kids
= d
->jobs
[daddy
];
184 const int pos
= d
->jobs
[daddy
].size();
185 d
->jobs
[daddy
] = kids
;
187 d
->unpublishedAdds
<< QPair
<int, int>(pos
, info
.parent
);
191 void JobTracker::jobEnded(const QString
&job
, const QString
&error
)
193 // this is called from dbus, so better be defensive
194 if (d
->disabled
|| !d
->jobs
.contains(job
) || !d
->infoList
.contains(job
)) {
198 JobInfo info
= d
->infoList
[job
];
199 if (error
.isEmpty()) {
200 info
.state
= JobInfo::Ended
;
202 info
.state
= JobInfo::Failed
;
205 info
.endedTimestamp
= QDateTime::currentDateTime();
206 d
->infoList
[job
] = info
;
208 d
->unpublishedUpdates
<< QPair
<int, int>(d
->jobs
[jobForId(info
.parent
)].size() - 1, info
.parent
);
212 void JobTracker::jobStarted(const QString
&job
)
214 // this is called from dbus, so better be defensive
215 if (d
->disabled
|| !d
->jobs
.contains(job
) || !d
->infoList
.contains(job
)) {
219 JobInfo info
= d
->infoList
[job
];
220 info
.state
= JobInfo::Running
;
221 info
.startedTimestamp
= QDateTime::currentDateTime();
222 d
->infoList
[job
] = info
;
224 d
->unpublishedUpdates
<< QPair
<int, int>(d
->jobs
[jobForId(info
.parent
)].size() - 1, info
.parent
);
228 QStringList
JobTracker::sessions() const
233 QList
<JobInfo
> JobTracker::jobs(int id
) const
235 if (d
->isSession(id
)) {
236 return jobs(sessionForId(id
));
238 return jobs(jobForId(id
));
241 QList
<JobInfo
> JobTracker::jobs(const QString
&parent
) const
243 assert(d
->jobs
.contains(parent
));
244 const QStringList jobs
= d
->jobs
.value(parent
);
245 QList
<JobInfo
> infoList
;
246 Q_FOREACH (const QString
&job
, jobs
) {
247 infoList
<< d
->infoList
.value(job
);
252 QStringList
JobTracker::jobNames(int id
) const
254 if (d
->isSession(id
)) {
255 return d
->jobs
.value(sessionForId(id
));
257 return d
->jobs
.value(jobForId(id
));
260 // only works on jobs
261 int JobTracker::idForJob(const QString
&job
) const
263 assert(d
->idToSequence
.contains(job
));
264 return d
->idToSequence
.value(job
);
267 QString
JobTracker::jobForId(int id
) const
269 if (d
->isSession(id
)) {
270 return sessionForId(id
);
272 assert(d
->sequenceToId
.contains(id
));
273 return d
->sequenceToId
.value(id
);
276 // To find a session, we take the offset in the list of sessions
277 // in order of appearance, add one, and make it negative. That
278 // way we can discern session ids from job ids and use -1 for invalid
279 int JobTracker::idForSession(const QString
&session
) const
281 assert(d
->sessions
.contains(session
));
282 return (d
->sessions
.indexOf(session
) + 2) * -1;
285 QString
JobTracker::sessionForId(int _id
) const
287 const int id
= (-_id
) - 2;
288 assert(d
->sessions
.size() > id
);
289 if (!d
->sessions
.isEmpty()) {
290 return d
->sessions
.at(id
);
296 int JobTracker::parentId(int id
) const
298 if (d
->isSession(id
)) {
301 const QString job
= d
->sequenceToId
.value(id
);
302 return d
->infoList
[job
].parent
;
307 JobInfo
JobTracker::info(int id
) const
309 return info(jobForId(id
));
312 JobInfo
JobTracker::info(const QString
&job
) const
314 assert(d
->infoList
.contains(job
));
315 return d
->infoList
.value(job
);
318 void JobTracker::triggerReset()
321 d
->idToSequence
.clear();
322 d
->sequenceToId
.clear();
329 void JobTracker::setEnabled(bool on
)
334 bool JobTracker::isEnabled() const
339 void JobTracker::signalUpdates()
341 Q_EMIT
added(d
->unpublishedAdds
);
342 Q_EMIT
updated(d
->unpublishedUpdates
);
343 d
->unpublishedAdds
.clear();
344 d
->unpublishedUpdates
.clear();