18 #ifndef QGSABSTRACTCONTENTCACHE_H
19 #define QGSABSTRACTCONTENTCACHE_H
21 #include "qgis_core.h"
30 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
33 #include <QRecursiveMutex>
40 #include <QNetworkReply>
82 int mFileModifiedCheckTimeout = 30000;
98 return other.
path == path;
104 virtual int dataSize()
const = 0;
109 virtual void dump()
const = 0;
153 void remoteContentFetched(
const QString &url );
161 virtual bool checkReply( QNetworkReply *reply,
const QString &path )
const
176 virtual void onRemoteContentFetched(
const QString &url,
bool success );
213 const QString &typeString = QString(),
214 long maxCacheSize = 20000000,
215 int fileModifiedCheckTimeout = 30000 )
217 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
218 , mMutex( QMutex::Recursive )
220 , mMaxCacheSize( maxCacheSize )
221 , mFileModifiedCheckTimeout( fileModifiedCheckTimeout )
222 , mTypeString( typeString.isEmpty() ? QObject::tr(
"Content" ) : typeString )
228 qDeleteAll( mEntryLookup );
239 if ( mLeastRecentEntry == mMostRecentEntry )
243 T *entry = mLeastRecentEntry;
244 while ( entry && ( mTotalSize > mMaxCacheSize ) )
247 entry =
static_cast< T *
>( entry->nextEntry );
249 takeEntryFromList( bkEntry );
250 mEntryLookup.remove( bkEntry->path, bkEntry );
251 mTotalSize -= bkEntry->dataSize();
269 QByteArray
getContent(
const QString &path,
const QByteArray &missingContent,
const QByteArray &fetchingContent,
bool blocking =
false )
const
275 if ( file.open( QIODevice::ReadOnly ) )
277 return file.readAll();
281 return missingContent;
286 if ( path.startsWith( QLatin1String(
"base64:" ), Qt::CaseInsensitive ) )
288 const QByteArray base64 = path.mid( 7 ).toLocal8Bit();
289 return QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
293 if ( !path.contains( QLatin1String(
"://" ) ) )
295 return missingContent;
298 const QUrl url( path );
299 if ( !url.isValid() )
301 return missingContent;
305 if ( url.scheme().compare( QLatin1String(
"file" ), Qt::CaseInsensitive ) == 0 )
307 file.setFileName( url.toLocalFile() );
310 if ( file.open( QIODevice::ReadOnly ) )
312 return file.readAll();
317 return missingContent;
320 const QMutexLocker locker( &mMutex );
323 if ( mPendingRemoteUrls.contains( path ) )
328 return fetchingContent;
333 for (
QgsTask *task : constActiveTasks )
336 if ( !task->description().endsWith( path ) )
346 if ( waitForTaskFinished( ncfTask ) )
348 if ( mRemoteContentCache.contains( path ) )
351 return *mRemoteContentCache[ path ];
362 if ( mRemoteContentCache.contains( path ) )
365 return *mRemoteContentCache[ path ];
368 mPendingRemoteUrls.insert( path );
370 QNetworkRequest request( url );
372 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
373 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
378 const QMutexLocker locker( &mMutex );
380 QNetworkReply *reply = task->
reply();
384 QMetaObject::invokeMethod(
const_cast< QgsAbstractContentCacheBase *
>( qobject_cast< const QgsAbstractContentCacheBase * >(
this ) ),
"onRemoteContentFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG(
bool,
false ) );
388 if ( reply->error() != QNetworkReply::NoError )
390 QgsMessageLog::logMessage( tr(
"%3 request failed [error: %1 - url: %2]" ).arg( reply->errorString(), path, mTypeString ), mTypeString );
396 const QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
397 if ( !status.isNull() && status.toInt() >= 400 )
399 const QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
400 QgsMessageLog::logMessage( tr(
"%4 request error [status: %1 - reason phrase: %2] for %3" ).arg( status.toInt() ).arg( phrase.toString(), path, mTypeString ), mTypeString );
401 mRemoteContentCache.insert( path,
new QByteArray( missingContent ) );
407 mRemoteContentCache.insert( path,
new QByteArray( missingContent ) );
414 const QByteArray ba = reply->readAll();
419 mRemoteContentCache.insert( path,
new QByteArray( ba ) );
421 QMetaObject::invokeMethod(
const_cast< QgsAbstractContentCacheBase *
>( qobject_cast< const QgsAbstractContentCacheBase * >(
this ) ),
"onRemoteContentFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG(
bool,
true ) );
429 if ( waitForTaskFinished( task ) )
431 if ( mRemoteContentCache.contains( path ) )
434 return *mRemoteContentCache[ path ];
438 return fetchingContent;
443 const QMutexLocker locker( &mMutex );
444 mPendingRemoteUrls.remove( url );
446 T *nextEntry = mLeastRecentEntry;
447 while ( T *entry = nextEntry )
449 nextEntry =
static_cast< T *
>( entry->nextEntry );
450 if ( entry->path == url )
452 takeEntryFromList( entry );
453 mEntryLookup.remove( entry->path, entry );
454 mTotalSize -= entry->dataSize();
504 const QString path = entryTemplate->path;
505 T *currentEntry =
nullptr;
506 const QList<T *> entries = mEntryLookup.values( path );
508 for ( T *cacheEntry : entries )
510 if ( cacheEntry->isEqual( entryTemplate ) )
512 if ( mFileModifiedCheckTimeout <= 0 || cacheEntry->fileModifiedLastCheckTimer.hasExpired( mFileModifiedCheckTimeout ) )
514 if ( !modified.isValid() )
515 modified = QFileInfo( path ).lastModified();
517 if ( cacheEntry->fileModified != modified )
520 cacheEntry->fileModifiedLastCheckTimer.restart();
522 currentEntry = cacheEntry;
530 currentEntry = insertCacheEntry( entryTemplate );
534 delete entryTemplate;
535 entryTemplate =
nullptr;
536 ( void )entryTemplate;
537 takeEntryFromList( currentEntry );
538 if ( !mMostRecentEntry )
540 mMostRecentEntry = currentEntry;
541 mLeastRecentEntry = currentEntry;
545 mMostRecentEntry->nextEntry = currentEntry;
546 currentEntry->previousEntry = mMostRecentEntry;
547 currentEntry->nextEntry =
nullptr;
548 mMostRecentEntry = currentEntry;
557 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
558 mutable QMutex mMutex;
566 long mMaxCacheSize = 20000000;
575 T *insertCacheEntry( T *entry )
577 entry->mFileModifiedCheckTimeout = mFileModifiedCheckTimeout;
579 if ( !entry->path.startsWith( QLatin1String(
"base64:" ) ) )
581 entry->fileModified = QFileInfo( entry->path ).lastModified();
582 entry->fileModifiedLastCheckTimer.start();
585 mEntryLookup.insert( entry->path, entry );
588 if ( !mMostRecentEntry )
590 mLeastRecentEntry = entry;
591 mMostRecentEntry = entry;
592 entry->previousEntry =
nullptr;
593 entry->nextEntry =
nullptr;
597 entry->previousEntry = mMostRecentEntry;
598 entry->nextEntry =
nullptr;
599 mMostRecentEntry->nextEntry = entry;
600 mMostRecentEntry = entry;
611 void takeEntryFromList( T *entry )
618 if ( entry->previousEntry )
620 entry->previousEntry->nextEntry = entry->nextEntry;
624 mLeastRecentEntry =
static_cast< T *
>( entry->nextEntry );
626 if ( entry->nextEntry )
628 entry->nextEntry->previousEntry = entry->previousEntry;
632 mMostRecentEntry =
static_cast< T *
>( entry->previousEntry );
639 void printEntryList()
641 QgsDebugMsg( QStringLiteral(
"****************cache entry list*************************" ) );
642 QgsDebugMsg(
"Cache size: " + QString::number( mTotalSize ) );
643 T *entry = mLeastRecentEntry;
648 entry = entry->nextEntry;
653 QMultiHash< QString, T * > mEntryLookup;
656 int mFileModifiedCheckTimeout = 30000;
660 T *mLeastRecentEntry =
nullptr;
661 T *mMostRecentEntry =
nullptr;
663 mutable QCache< QString, QByteArray > mRemoteContentCache;
664 mutable QSet< QString > mPendingRemoteUrls;
668 friend class TestQgsSvgCache;
669 friend class TestQgsImageCache;
674 #endif // QGSABSTRACTCONTENTCACHE_H