18 #ifndef QGSABSTRACTCONTENTCACHE_H
19 #define QGSABSTRACTCONTENTCACHE_H
21 #include "qgis_core.h"
35 #include <QNetworkReply>
75 int mFileModifiedCheckTimeout = 30000;
91 return other.
path == path;
97 virtual int dataSize()
const = 0;
102 virtual void dump()
const = 0;
146 void remoteContentFetched(
const QString &url );
154 virtual bool checkReply( QNetworkReply *reply,
const QString &path )
const
169 virtual void onRemoteContentFetched(
const QString &url,
bool success );
206 const QString &typeString = QString(),
207 long maxCacheSize = 20000000,
208 int fileModifiedCheckTimeout = 30000 )
210 , mMutex( QMutex::Recursive )
211 , mMaxCacheSize( maxCacheSize )
212 , mFileModifiedCheckTimeout( fileModifiedCheckTimeout )
213 , mTypeString( typeString.isEmpty() ? QObject::tr(
"Content" ) : typeString )
219 qDeleteAll( mEntryLookup );
230 if ( mLeastRecentEntry == mMostRecentEntry )
234 T *entry = mLeastRecentEntry;
235 while ( entry && ( mTotalSize > mMaxCacheSize ) )
238 entry =
static_cast< T *
>( entry->nextEntry );
240 takeEntryFromList( bkEntry );
241 mEntryLookup.remove( bkEntry->path, bkEntry );
242 mTotalSize -= bkEntry->dataSize();
260 QByteArray
getContent(
const QString &path,
const QByteArray &missingContent,
const QByteArray &fetchingContent,
bool blocking =
false )
const
266 if ( file.open( QIODevice::ReadOnly ) )
268 return file.readAll();
272 return missingContent;
277 if ( path.startsWith( QLatin1String(
"base64:" ), Qt::CaseInsensitive ) )
279 QByteArray base64 = path.mid( 7 ).toLocal8Bit();
280 return QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
284 if ( !path.contains( QLatin1String(
"://" ) ) )
286 return missingContent;
290 if ( !url.isValid() )
292 return missingContent;
296 if ( url.scheme().compare( QLatin1String(
"file" ), Qt::CaseInsensitive ) == 0 )
298 file.setFileName( url.toLocalFile() );
301 if ( file.open( QIODevice::ReadOnly ) )
303 return file.readAll();
308 return missingContent;
311 QMutexLocker locker( &mMutex );
314 if ( mPendingRemoteUrls.contains( path ) )
319 return fetchingContent;
324 for (
QgsTask *task : constActiveTasks )
327 if ( !task->description().endsWith( path ) )
337 if ( waitForTaskFinished( ncfTask ) )
339 if ( mRemoteContentCache.contains( path ) )
342 return *mRemoteContentCache[ path ];
353 if ( mRemoteContentCache.contains( path ) )
356 return *mRemoteContentCache[ path ];
359 mPendingRemoteUrls.insert( path );
361 QNetworkRequest request( url );
363 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
364 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
369 QMutexLocker locker( &mMutex );
371 QNetworkReply *reply = task->
reply();
375 QMetaObject::invokeMethod(
const_cast< QgsAbstractContentCacheBase *
>( qobject_cast< const QgsAbstractContentCacheBase * >(
this ) ),
"onRemoteContentFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG(
bool,
false ) );
379 if ( reply->error() != QNetworkReply::NoError )
381 QgsMessageLog::logMessage( tr(
"%3 request failed [error: %1 - url: %2]" ).arg( reply->errorString(), path, mTypeString ), mTypeString );
387 QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
388 if ( !status.isNull() && status.toInt() >= 400 )
390 QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
391 QgsMessageLog::logMessage( tr(
"%4 request error [status: %1 - reason phrase: %2] for %3" ).arg( status.toInt() ).arg( phrase.toString(), path, mTypeString ), mTypeString );
392 mRemoteContentCache.insert( path,
new QByteArray( missingContent ) );
398 mRemoteContentCache.insert( path,
new QByteArray( missingContent ) );
405 const QByteArray ba = reply->readAll();
410 mRemoteContentCache.insert( path,
new QByteArray( ba ) );
412 QMetaObject::invokeMethod(
const_cast< QgsAbstractContentCacheBase *
>( qobject_cast< const QgsAbstractContentCacheBase * >(
this ) ),
"onRemoteContentFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG(
bool,
true ) );
420 if ( waitForTaskFinished( task ) )
422 if ( mRemoteContentCache.contains( path ) )
425 return *mRemoteContentCache[ path ];
429 return fetchingContent;
434 QMutexLocker locker( &mMutex );
435 mPendingRemoteUrls.remove( url );
437 T *nextEntry = mLeastRecentEntry;
438 while ( T *entry = nextEntry )
440 nextEntry =
static_cast< T *
>( entry->nextEntry );
441 if ( entry->path == url )
443 takeEntryFromList( entry );
444 mEntryLookup.remove( entry->path, entry );
445 mTotalSize -= entry->dataSize();
495 const QString path = entryTemplate->path;
496 T *currentEntry =
nullptr;
497 const QList<T *> entries = mEntryLookup.values( path );
499 for ( T *cacheEntry : entries )
501 if ( cacheEntry->isEqual( entryTemplate ) )
503 if ( mFileModifiedCheckTimeout <= 0 || cacheEntry->fileModifiedLastCheckTimer.hasExpired( mFileModifiedCheckTimeout ) )
505 if ( !modified.isValid() )
506 modified = QFileInfo( path ).lastModified();
508 if ( cacheEntry->fileModified != modified )
511 cacheEntry->fileModifiedLastCheckTimer.restart();
513 currentEntry = cacheEntry;
521 currentEntry = insertCacheEntry( entryTemplate );
525 delete entryTemplate;
526 entryTemplate =
nullptr;
527 ( void )entryTemplate;
528 takeEntryFromList( currentEntry );
529 if ( !mMostRecentEntry )
531 mMostRecentEntry = currentEntry;
532 mLeastRecentEntry = currentEntry;
536 mMostRecentEntry->nextEntry = currentEntry;
537 currentEntry->previousEntry = mMostRecentEntry;
538 currentEntry->nextEntry =
nullptr;
539 mMostRecentEntry = currentEntry;
554 long mMaxCacheSize = 20000000;
563 T *insertCacheEntry( T *entry )
565 entry->mFileModifiedCheckTimeout = mFileModifiedCheckTimeout;
567 if ( !entry->path.startsWith( QStringLiteral(
"base64:" ) ) )
569 entry->fileModified = QFileInfo( entry->path ).lastModified();
570 entry->fileModifiedLastCheckTimer.start();
573 mEntryLookup.insert( entry->path, entry );
576 if ( !mMostRecentEntry )
578 mLeastRecentEntry = entry;
579 mMostRecentEntry = entry;
580 entry->previousEntry =
nullptr;
581 entry->nextEntry =
nullptr;
585 entry->previousEntry = mMostRecentEntry;
586 entry->nextEntry =
nullptr;
587 mMostRecentEntry->nextEntry = entry;
588 mMostRecentEntry = entry;
599 void takeEntryFromList( T *entry )
606 if ( entry->previousEntry )
608 entry->previousEntry->nextEntry = entry->nextEntry;
612 mLeastRecentEntry =
static_cast< T *
>( entry->nextEntry );
614 if ( entry->nextEntry )
616 entry->nextEntry->previousEntry = entry->previousEntry;
620 mMostRecentEntry =
static_cast< T *
>( entry->previousEntry );
627 void printEntryList()
629 QgsDebugMsg( QStringLiteral(
"****************cache entry list*************************" ) );
630 QgsDebugMsg(
"Cache size: " + QString::number( mTotalSize ) );
631 T *entry = mLeastRecentEntry;
636 entry = entry->nextEntry;
641 QMultiHash< QString, T * > mEntryLookup;
644 int mFileModifiedCheckTimeout = 30000;
648 T *mLeastRecentEntry =
nullptr;
649 T *mMostRecentEntry =
nullptr;
651 mutable QCache< QString, QByteArray > mRemoteContentCache;
652 mutable QSet< QString > mPendingRemoteUrls;
656 friend class TestQgsSvgCache;
657 friend class TestQgsImageCache;
662 #endif // QGSABSTRACTCONTENTCACHE_H