18 #ifndef QGSABSTRACTCONTENTCACHE_H 19 #define QGSABSTRACTCONTENTCACHE_H 21 #include "qgis_core.h" 35 #include <QNetworkReply> 75 int mFileModifiedCheckTimeout = 30000;
91 bool equal = other.
path == path;
92 if ( equal && ( mFileModifiedCheckTimeout <= 0 || fileModifiedLastCheckTimer.hasExpired( mFileModifiedCheckTimeout ) ) )
101 virtual int dataSize()
const = 0;
106 virtual void dump()
const = 0;
150 void remoteContentFetched(
const QString &url );
158 virtual bool checkReply( QNetworkReply *reply,
const QString &path )
const 173 virtual void onRemoteContentFetched(
const QString &url,
bool success );
210 const QString &typeString = QString(),
211 long maxCacheSize = 20000000,
212 int fileModifiedCheckTimeout = 30000 )
214 , mMutex( QMutex::Recursive )
215 , mMaxCacheSize( maxCacheSize )
216 , mFileModifiedCheckTimeout( fileModifiedCheckTimeout )
217 , mTypeString( typeString.isEmpty() ? QObject::tr(
"Content" ) : typeString )
223 qDeleteAll( mEntryLookup );
234 if ( mLeastRecentEntry == mMostRecentEntry )
238 T *entry = mLeastRecentEntry;
239 while ( entry && ( mTotalSize > mMaxCacheSize ) )
242 entry =
static_cast< T *
>( entry->nextEntry );
244 takeEntryFromList( bkEntry );
245 mEntryLookup.remove( bkEntry->path, bkEntry );
246 mTotalSize -= bkEntry->dataSize();
260 QByteArray
getContent(
const QString &path,
const QByteArray &missingContent,
const QByteArray &fetchingContent )
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 ) )
315 return fetchingContent;
317 if ( mRemoteContentCache.contains( path ) )
320 return *mRemoteContentCache[ path ];
323 mPendingRemoteUrls.insert( path );
325 QNetworkRequest request( url );
327 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
328 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
333 QMutexLocker locker( &mMutex );
335 QNetworkReply *reply = task->
reply();
339 QMetaObject::invokeMethod( const_cast< QgsAbstractContentCacheBase * >( qobject_cast< const QgsAbstractContentCacheBase * >(
this ) ),
"onRemoteContentFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG(
bool,
false ) );
343 if ( reply->error() != QNetworkReply::NoError )
345 QgsMessageLog::logMessage( tr(
"%3 request failed [error: %1 - url: %2]" ).arg( reply->errorString(), path, mTypeString ), mTypeString );
351 QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
352 if ( !status.isNull() && status.toInt() >= 400 )
354 QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
355 QgsMessageLog::logMessage( tr(
"%4 request error [status: %1 - reason phrase: %2] for %3" ).arg( status.toInt() ).arg( phrase.toString(), path, mTypeString ), mTypeString );
356 mRemoteContentCache.insert( path,
new QByteArray( missingContent ) );
362 mRemoteContentCache.insert( path,
new QByteArray( missingContent ) );
369 mRemoteContentCache.insert( path,
new QByteArray( reply->readAll() ) );
371 QMetaObject::invokeMethod( const_cast< QgsAbstractContentCacheBase * >( qobject_cast< const QgsAbstractContentCacheBase * >(
this ) ),
"onRemoteContentFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG(
bool,
true ) );
375 return fetchingContent;
380 QMutexLocker locker( &mMutex );
381 mPendingRemoteUrls.remove( url );
383 T *nextEntry = mLeastRecentEntry;
384 while ( T *entry = nextEntry )
386 nextEntry =
static_cast< T *
>( entry->nextEntry );
387 if ( entry->path == url )
389 takeEntryFromList( entry );
390 mEntryLookup.remove( entry->path, entry );
391 mTotalSize -= entry->dataSize();
412 const QString path = entryTemplate->path;
413 T *currentEntry =
nullptr;
414 const QList<T *> entries = mEntryLookup.values( path );
416 for ( T *cacheEntry : entries )
418 if ( cacheEntry->isEqual( entryTemplate ) )
420 if ( mFileModifiedCheckTimeout <= 0 || cacheEntry->fileModifiedLastCheckTimer.hasExpired( mFileModifiedCheckTimeout ) )
422 if ( !modified.isValid() )
423 modified = QFileInfo( path ).lastModified();
425 if ( cacheEntry->fileModified != modified )
428 currentEntry = cacheEntry;
436 currentEntry = insertCacheEntry( entryTemplate );
440 delete entryTemplate;
441 entryTemplate =
nullptr;
442 takeEntryFromList( currentEntry );
443 if ( !mMostRecentEntry )
445 mMostRecentEntry = currentEntry;
446 mLeastRecentEntry = currentEntry;
450 mMostRecentEntry->nextEntry = currentEntry;
451 currentEntry->previousEntry = mMostRecentEntry;
452 currentEntry->nextEntry =
nullptr;
453 mMostRecentEntry = currentEntry;
468 long mMaxCacheSize = 20000000;
477 T *insertCacheEntry( T *entry )
479 entry->mFileModifiedCheckTimeout = mFileModifiedCheckTimeout;
481 mEntryLookup.insert( entry->path, entry );
484 if ( !mMostRecentEntry )
486 mLeastRecentEntry = entry;
487 mMostRecentEntry = entry;
488 entry->previousEntry =
nullptr;
489 entry->nextEntry =
nullptr;
493 entry->previousEntry = mMostRecentEntry;
494 entry->nextEntry =
nullptr;
495 mMostRecentEntry->nextEntry = entry;
496 mMostRecentEntry = entry;
507 void takeEntryFromList( T *entry )
514 if ( entry->previousEntry )
516 entry->previousEntry->nextEntry = entry->nextEntry;
520 mLeastRecentEntry =
static_cast< T *
>( entry->nextEntry );
522 if ( entry->nextEntry )
524 entry->nextEntry->previousEntry = entry->previousEntry;
528 mMostRecentEntry =
static_cast< T *
>( entry->previousEntry );
535 void printEntryList()
537 QgsDebugMsg( QStringLiteral(
"****************cache entry list*************************" ) );
538 QgsDebugMsg(
"Cache size: " + QString::number( mTotalSize ) );
539 T *entry = mLeastRecentEntry;
544 entry = entry->nextEntry;
549 QMultiHash< QString, T * > mEntryLookup;
552 int mFileModifiedCheckTimeout = 30000;
556 T *mLeastRecentEntry =
nullptr;
557 T *mMostRecentEntry =
nullptr;
559 mutable QCache< QString, QByteArray > mRemoteContentCache;
560 mutable QSet< QString > mPendingRemoteUrls;
564 friend class TestQgsSvgCache;
565 friend class TestQgsImageCache;
570 #endif // QGSABSTRACTCONTENTCACHE_H #define QgsSetRequestInitiatorClass(request, _class)
Abstract base class for file content caches, such as SVG or raster image caches.
void fetched()
Emitted when the network content has been fetched, regardless of whether the fetch was successful or ...
QgsAbstractContentCache(QObject *parent=nullptr, const QString &typeString=QString(), long maxCacheSize=20000000, int fileModifiedCheckTimeout=30000)
Constructor for QgsAbstractContentCache, with the specified parent object.
Base class for entries in a QgsAbstractContentCache.
~QgsAbstractContentCache() override
void remoteContentFetched(const QString &url)
Emitted when the cache has finished retrieving content from a remote url.
QNetworkReply * reply()
Returns the network reply.
Handles HTTP network content fetching in a background task.
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
void onRemoteContentFetched(const QString &url, bool success) override
Triggered after remote content (i.e.
T * findExistingEntry(T *entryTemplate)
Returns the existing entry from the cache which matches entryTemplate (deleting entryTemplate when do...
QDateTime fileModified
Timestamp when file was last modified.
QByteArray getContent(const QString &path, const QByteArray &missingContent, const QByteArray &fetchingContent) const
Gets the file content corresponding to the given path.
A QObject derived base class for QgsAbstractContentCache.
virtual bool checkReply(QNetworkReply *reply, const QString &path) const
Runs additional checks on a network reply to ensure that the reply content is consistent with that re...
QElapsedTimer fileModifiedLastCheckTimer
Time since last check of file modified date.
bool operator==(const QgsAbstractContentCacheEntry &other) const
void trimToMaximumSize()
Removes the least used cache entries until the maximum cache size is under the predefined size limit...
QString path
Represents the absolute path to a file, a remote URL, or a base64 encoded string. ...