File indexing completed on 2024-04-14 03:47:42

0001 // SPDX-FileCopyrightText: 2009 Jens-Michael Hoffmann <jmho@c-xx.com>
0002 //
0003 // SPDX-License-Identifier: LGPL-2.1-or-later
0004 
0005 #include "DownloadQueueSet.h"
0006 
0007 #include "MarbleDebug.h"
0008 
0009 #include "HttpJob.h"
0010 
0011 namespace Marble
0012 {
0013 
0014 DownloadQueueSet::DownloadQueueSet( QObject * const parent )
0015     : QObject( parent )
0016 {
0017 }
0018 
0019 DownloadQueueSet::DownloadQueueSet( DownloadPolicy const & policy, QObject * const parent )
0020     : QObject( parent ),
0021       m_downloadPolicy( policy )
0022 {
0023 }
0024 
0025 DownloadQueueSet::~DownloadQueueSet()
0026 {
0027     // todo: delete HttpJobs
0028 }
0029 
0030 DownloadPolicy DownloadQueueSet::downloadPolicy() const
0031 {
0032     return m_downloadPolicy;
0033 }
0034 
0035 void DownloadQueueSet::setDownloadPolicy( DownloadPolicy const & policy )
0036 {
0037     m_downloadPolicy = policy;
0038 }
0039 
0040 bool DownloadQueueSet::canAcceptJob( const QUrl& sourceUrl,
0041                                      const QString& destinationFileName ) const
0042 {
0043     if ( jobIsQueued( destinationFileName )) {
0044         mDebug() << "Download rejected: It's in the queue already:"
0045                  << destinationFileName;
0046         return false;
0047     }
0048     if ( jobIsWaitingForRetry( destinationFileName )) {
0049         mDebug() << "Download rejected: Will try to download again in some time:"
0050                  << destinationFileName;
0051         return false;
0052     }
0053     if ( jobIsActive( destinationFileName )) {
0054         mDebug() << "Download rejected: It's being downloaded already:"
0055                  << destinationFileName;
0056         return false;
0057     }
0058     if ( jobIsBlackListed( sourceUrl )) {
0059         mDebug() << "Download rejected: Blacklisted.";
0060         return false;
0061     }
0062     return true;
0063 }
0064 
0065 void DownloadQueueSet::addJob( HttpJob * const job )
0066 {
0067     m_jobs.push( job );
0068     mDebug() << "addJob: new job queue size:" << m_jobs.count();
0069     emit jobAdded();
0070     emit progressChanged( m_activeJobs.size(), m_jobs.count() );
0071     activateJobs();
0072 }
0073 
0074 void DownloadQueueSet::activateJobs()
0075 {
0076     while ( !m_jobs.isEmpty()
0077             && m_activeJobs.count() < m_downloadPolicy.maximumConnections() )
0078     {
0079         HttpJob * const job = m_jobs.pop();
0080         activateJob( job );
0081     }
0082 }
0083 
0084 void DownloadQueueSet::retryJobs()
0085 {
0086     while ( !m_retryQueue.isEmpty() ) {
0087         HttpJob * const job = m_retryQueue.dequeue();
0088         mDebug() << "Requeuing" << job->destinationFileName();
0089         // FIXME: addJob calls activateJobs every time
0090         addJob( job );
0091     }
0092 }
0093 
0094 void DownloadQueueSet::purgeJobs()
0095 {
0096     // purge all waiting jobs
0097     while( !m_jobs.isEmpty() ) {
0098         HttpJob * const job = m_jobs.pop();
0099         job->deleteLater();
0100     }
0101 
0102     // purge all retry jobs
0103     qDeleteAll( m_retryQueue );
0104     m_retryQueue.clear();
0105 
0106     // cancel all current jobs
0107     while( !m_activeJobs.isEmpty() ) {
0108         deactivateJob( m_activeJobs.first() );
0109     }
0110 
0111     emit progressChanged( m_activeJobs.size(), m_jobs.count() );
0112 }
0113 
0114 void DownloadQueueSet::finishJob( HttpJob * job, const QByteArray& data )
0115 {
0116     mDebug() << job->sourceUrl() << job->destinationFileName();
0117 
0118     deactivateJob( job );
0119     emit jobRemoved();
0120     emit jobFinished( data, job->destinationFileName(), job->initiatorId() );
0121     job->deleteLater();
0122     activateJobs();
0123 }
0124 
0125 void DownloadQueueSet::redirectJob( HttpJob * job, const QUrl& newSourceUrl )
0126 {
0127     mDebug() << job->sourceUrl() << " -> " << newSourceUrl;
0128 
0129     deactivateJob( job );
0130     emit jobRemoved();
0131     emit jobRedirected( newSourceUrl, job->destinationFileName(), job->initiatorId(),
0132                         job->downloadUsage() );
0133     job->deleteLater();
0134 }
0135 
0136 void DownloadQueueSet::retryOrBlacklistJob( HttpJob * job, const int errorCode )
0137 {
0138     Q_ASSERT( errorCode != 0 );
0139     Q_ASSERT( !m_retryQueue.contains( job ));
0140 
0141     deactivateJob( job );
0142     emit jobRemoved();
0143 
0144     if ( job->tryAgain() ) {
0145         mDebug() << QString( "Download of %1 to %2 failed, but trying again soon" )
0146             .arg( job->sourceUrl().toString(), job->destinationFileName() );
0147         m_retryQueue.enqueue( job );
0148         emit jobRetry();
0149     }
0150     else {
0151         mDebug() << "JOB-address: " << job
0152                  << "Blacklist-size:" << m_jobBlackList.size()
0153                  << "err:" << errorCode;
0154         m_jobBlackList.insert( job->sourceUrl().toString() );
0155         mDebug() << QString( "Download of %1 Blacklisted. "
0156                              "Number of blacklist items: %2" )
0157             .arg( job->destinationFileName() )
0158             .arg( m_jobBlackList.size() );
0159 
0160         job->deleteLater();
0161     }
0162     activateJobs();
0163 }
0164 
0165 void DownloadQueueSet::activateJob( HttpJob * const job )
0166 {
0167     m_activeJobs.push_back( job );
0168     emit progressChanged( m_activeJobs.size(), m_jobs.count() );
0169 
0170     connect( job, SIGNAL(jobDone(HttpJob*,int)),
0171              SLOT(retryOrBlacklistJob(HttpJob*,int)));
0172     connect( job, SIGNAL(redirected(HttpJob*,QUrl)),
0173              SLOT(redirectJob(HttpJob*,QUrl)));
0174     connect( job, SIGNAL(dataReceived(HttpJob*,QByteArray)),
0175              SLOT(finishJob(HttpJob*,QByteArray)));
0176 
0177     job->execute();
0178 }
0179 
0180 /**
0181    pre condition: - job is in m_activeJobs
0182                   - job's signal are connected to our slots
0183    post condition: - job is not in m_activeJobs anymore (and btw not
0184                      in any other queue)
0185                    - job's signals are disconnected from our slots
0186  */
0187 void DownloadQueueSet::deactivateJob( HttpJob * const job )
0188 {
0189     const bool disconnected = job->disconnect();
0190     Q_ASSERT( disconnected );
0191     Q_UNUSED( disconnected ); // for Q_ASSERT in release mode
0192     const bool removed = m_activeJobs.removeOne( job );
0193     Q_ASSERT( removed );
0194     Q_UNUSED( removed ); // for Q_ASSERT in release mode
0195     emit progressChanged( m_activeJobs.size(), m_jobs.count() );
0196 }
0197 
0198 bool DownloadQueueSet::jobIsActive( QString const & destinationFileName ) const
0199 {
0200     QList<HttpJob*>::const_iterator pos = m_activeJobs.constBegin();
0201     QList<HttpJob*>::const_iterator const end = m_activeJobs.constEnd();
0202     for (; pos != end; ++pos) {
0203         if ( (*pos)->destinationFileName() == destinationFileName ) {
0204             return true;
0205         }
0206     }
0207     return false;
0208 }
0209 
0210 inline bool DownloadQueueSet::jobIsQueued( QString const & destinationFileName ) const
0211 {
0212     return m_jobs.contains( destinationFileName );
0213 }
0214 
0215 bool DownloadQueueSet::jobIsWaitingForRetry( QString const & destinationFileName ) const
0216 {
0217     QList<HttpJob*>::const_iterator pos = m_retryQueue.constBegin();
0218     QList<HttpJob*>::const_iterator const end = m_retryQueue.constEnd();
0219     for (; pos != end; ++pos) {
0220         if ( (*pos)->destinationFileName() == destinationFileName ) {
0221             return true;
0222         }
0223     }
0224     return false;
0225 }
0226 
0227 bool DownloadQueueSet::jobIsBlackListed( const QUrl& sourceUrl ) const
0228 {
0229     QSet<QString>::const_iterator const pos =
0230         m_jobBlackList.constFind( sourceUrl.toString() );
0231     return pos != m_jobBlackList.constEnd();
0232 }
0233 
0234 
0235 inline bool DownloadQueueSet::JobStack::contains( const QString& destinationFileName ) const
0236 {
0237     return m_jobsContent.contains( destinationFileName );
0238 }
0239 
0240 inline int DownloadQueueSet::JobStack::count() const
0241 {
0242     return m_jobs.count();
0243 }
0244 
0245 inline bool DownloadQueueSet::JobStack::isEmpty() const
0246 {
0247     return m_jobs.isEmpty();
0248 }
0249 
0250 inline HttpJob * DownloadQueueSet::JobStack::pop()
0251 {
0252     HttpJob * const job = m_jobs.pop();
0253     bool const removed = m_jobsContent.remove( job->destinationFileName() );
0254     Q_UNUSED( removed ); // for Q_ASSERT in release mode
0255     Q_ASSERT( removed );
0256     return job;
0257 }
0258 
0259 inline void DownloadQueueSet::JobStack::push( HttpJob * const job )
0260 {
0261     m_jobs.push( job );
0262     m_jobsContent.insert( job->destinationFileName() );
0263 }
0264 
0265 
0266 }
0267 
0268 #include "moc_DownloadQueueSet.cpp"