Marble

DownloadQueueSet.cpp
1// SPDX-FileCopyrightText: 2009 Jens-Michael Hoffmann <jmho@c-xx.com>
2//
3// SPDX-License-Identifier: LGPL-2.1-or-later
4
5#include "DownloadQueueSet.h"
6
7#include "MarbleDebug.h"
8
9#include "HttpJob.h"
10
11namespace Marble
12{
13
14DownloadQueueSet::DownloadQueueSet(QObject *const parent)
15 : QObject(parent)
16{
17}
18
19DownloadQueueSet::DownloadQueueSet(DownloadPolicy const &policy, QObject *const parent)
20 : QObject(parent)
21 , m_downloadPolicy(policy)
22{
23}
24
25DownloadQueueSet::~DownloadQueueSet()
26{
27 // todo: delete HttpJobs
28}
29
30DownloadPolicy DownloadQueueSet::downloadPolicy() const
31{
32 return m_downloadPolicy;
33}
34
35void DownloadQueueSet::setDownloadPolicy(DownloadPolicy const &policy)
36{
37 m_downloadPolicy = policy;
38}
39
40bool DownloadQueueSet::canAcceptJob(const QUrl &sourceUrl, const QString &destinationFileName) const
41{
42 if (jobIsQueued(destinationFileName)) {
43 mDebug() << "Download rejected: It's in the queue already:" << destinationFileName;
44 return false;
45 }
46 if (jobIsWaitingForRetry(destinationFileName)) {
47 mDebug() << "Download rejected: Will try to download again in some time:" << destinationFileName;
48 return false;
49 }
50 if (jobIsActive(destinationFileName)) {
51 mDebug() << "Download rejected: It's being downloaded already:" << destinationFileName;
52 return false;
53 }
54 if (jobIsBlackListed(sourceUrl)) {
55 mDebug() << "Download rejected: Blacklisted.";
56 return false;
57 }
58 return true;
59}
60
61void DownloadQueueSet::addJob(HttpJob *const job)
62{
63 m_jobs.push(job);
64 mDebug() << "addJob: new job queue size:" << m_jobs.count();
65 Q_EMIT jobAdded();
66 Q_EMIT progressChanged(m_activeJobs.size(), m_jobs.count());
67 activateJobs();
68}
69
70void DownloadQueueSet::activateJobs()
71{
72 while (!m_jobs.isEmpty() && m_activeJobs.count() < m_downloadPolicy.maximumConnections()) {
73 HttpJob *const job = m_jobs.pop();
74 activateJob(job);
75 }
76}
77
78void DownloadQueueSet::retryJobs()
79{
80 while (!m_retryQueue.isEmpty()) {
81 HttpJob *const job = m_retryQueue.dequeue();
82 mDebug() << "Requeuing" << job->destinationFileName();
83 // FIXME: addJob calls activateJobs every time
84 addJob(job);
85 }
86}
87
88void DownloadQueueSet::purgeJobs()
89{
90 // purge all waiting jobs
91 while (!m_jobs.isEmpty()) {
92 HttpJob *const job = m_jobs.pop();
93 job->deleteLater();
94 }
95
96 // purge all retry jobs
97 qDeleteAll(m_retryQueue);
98 m_retryQueue.clear();
99
100 // cancel all current jobs
101 while (!m_activeJobs.isEmpty()) {
102 deactivateJob(m_activeJobs.first());
103 }
104
105 Q_EMIT progressChanged(m_activeJobs.size(), m_jobs.count());
106}
107
108void DownloadQueueSet::finishJob(HttpJob *job, const QByteArray &data)
109{
110 mDebug() << job->sourceUrl() << job->destinationFileName();
111
112 deactivateJob(job);
113 Q_EMIT jobRemoved();
114 Q_EMIT jobFinished(data, job->destinationFileName(), job->initiatorId());
115 job->deleteLater();
116 activateJobs();
117}
118
119void DownloadQueueSet::redirectJob(HttpJob *job, const QUrl &newSourceUrl)
120{
121 mDebug() << job->sourceUrl() << " -> " << newSourceUrl;
122
123 deactivateJob(job);
124 Q_EMIT jobRemoved();
125 Q_EMIT jobRedirected(newSourceUrl, job->destinationFileName(), job->initiatorId(), job->downloadUsage());
126 job->deleteLater();
127}
128
129void DownloadQueueSet::retryOrBlacklistJob(HttpJob *job, const int errorCode)
130{
131 Q_ASSERT(errorCode != 0);
132 Q_ASSERT(!m_retryQueue.contains(job));
133
134 deactivateJob(job);
135 Q_EMIT jobRemoved();
136
137 if (job->tryAgain()) {
138 mDebug() << QStringLiteral("Download of %1 to %2 failed, but trying again soon").arg(job->sourceUrl().toString(), job->destinationFileName());
139 m_retryQueue.enqueue(job);
140 Q_EMIT jobRetry();
141 } else {
142 mDebug() << "JOB-address: " << job << "Blacklist-size:" << m_jobBlackList.size() << "err:" << errorCode;
143 m_jobBlackList.insert(job->sourceUrl().toString());
144 mDebug() << QStringLiteral(
145 "Download of %1 Blacklisted. "
146 "Number of blacklist items: %2")
147 .arg(job->destinationFileName())
148 .arg(m_jobBlackList.size());
149
150 job->deleteLater();
151 }
152 activateJobs();
153}
154
155void DownloadQueueSet::activateJob(HttpJob *const job)
156{
157 m_activeJobs.push_back(job);
158 Q_EMIT progressChanged(m_activeJobs.size(), m_jobs.count());
159
160 connect(job, SIGNAL(jobDone(HttpJob *, int)), SLOT(retryOrBlacklistJob(HttpJob *, int)));
161 connect(job, SIGNAL(redirected(HttpJob *, QUrl)), SLOT(redirectJob(HttpJob *, QUrl)));
162 connect(job, SIGNAL(dataReceived(HttpJob *, QByteArray)), SLOT(finishJob(HttpJob *, QByteArray)));
163
164 job->execute();
165}
166
167/**
168 pre condition: - job is in m_activeJobs
169 - job's signal are connected to our slots
170 post condition: - job is not in m_activeJobs anymore (and btw not
171 in any other queue)
172 - job's signals are disconnected from our slots
173 */
174void DownloadQueueSet::deactivateJob(HttpJob *const job)
175{
176 const bool disconnected = job->disconnect();
177 Q_ASSERT(disconnected);
178 Q_UNUSED(disconnected); // for Q_ASSERT in release mode
179 const bool removed = m_activeJobs.removeOne(job);
180 Q_ASSERT(removed);
181 Q_UNUSED(removed); // for Q_ASSERT in release mode
182 Q_EMIT progressChanged(m_activeJobs.size(), m_jobs.count());
183}
184
185bool DownloadQueueSet::jobIsActive(QString const &destinationFileName) const
186{
187 QList<HttpJob *>::const_iterator pos = m_activeJobs.constBegin();
188 QList<HttpJob *>::const_iterator const end = m_activeJobs.constEnd();
189 for (; pos != end; ++pos) {
190 if ((*pos)->destinationFileName() == destinationFileName) {
191 return true;
192 }
193 }
194 return false;
195}
196
197inline bool DownloadQueueSet::jobIsQueued(QString const &destinationFileName) const
198{
199 return m_jobs.contains(destinationFileName);
200}
201
202bool DownloadQueueSet::jobIsWaitingForRetry(QString const &destinationFileName) const
203{
204 QList<HttpJob *>::const_iterator pos = m_retryQueue.constBegin();
205 QList<HttpJob *>::const_iterator const end = m_retryQueue.constEnd();
206 for (; pos != end; ++pos) {
207 if ((*pos)->destinationFileName() == destinationFileName) {
208 return true;
209 }
210 }
211 return false;
212}
213
214bool DownloadQueueSet::jobIsBlackListed(const QUrl &sourceUrl) const
215{
216 QSet<QString>::const_iterator const pos = m_jobBlackList.constFind(sourceUrl.toString());
217 return pos != m_jobBlackList.constEnd();
218}
219
220inline bool DownloadQueueSet::JobStack::contains(const QString &destinationFileName) const
221{
222 return m_jobsContent.contains(destinationFileName);
223}
224
225inline int DownloadQueueSet::JobStack::count() const
226{
227 return m_jobs.count();
228}
229
230inline bool DownloadQueueSet::JobStack::isEmpty() const
231{
232 return m_jobs.isEmpty();
233}
234
235inline HttpJob *DownloadQueueSet::JobStack::pop()
236{
237 HttpJob *const job = m_jobs.pop();
238 bool const removed = m_jobsContent.remove(job->destinationFileName());
239 Q_UNUSED(removed); // for Q_ASSERT in release mode
240 Q_ASSERT(removed);
241 return job;
242}
243
244inline void DownloadQueueSet::JobStack::push(HttpJob *const job)
245{
246 m_jobs.push(job);
247 m_jobsContent.insert(job->destinationFileName());
248}
249
250}
251
252#include "moc_DownloadQueueSet.cpp"
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
Binds a QML item to a specific geodetic location in screen coordinates.
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QString toString(FormattingOptions options) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 8 2024 12:02:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.