QGIS API Documentation 3.43.0-Master (b60ef06885e)
qgsconfigcache.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsconfigcache.cpp
3 ------------------
4 begin : July 24th, 2010
5 copyright : (C) 2010 by Marco Hugentobler
6 email : marco dot hugentobler at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17#include "qgsconfigcache.h"
18#include "moc_qgsconfigcache.cpp"
19#include "qgsmessagelog.h"
20#include "qgsserverexception.h"
23
24#include <QFile>
25
26QgsConfigCache *QgsConfigCache::sInstance = nullptr;
27
28
30{
32 if ( settings && settings->projectCacheStrategy() == QLatin1String( "periodic" ) )
33 {
34 strategy = new QgsPeriodicCacheStrategy( settings->projectCacheCheckInterval() );
36 QStringLiteral( "Initializing 'periodic' cache strategy" ),
37 QStringLiteral( "Server" ), Qgis::MessageLevel::Info
38 );
39 }
40 else if ( settings && settings->projectCacheStrategy() == QLatin1String( "off" ) )
41 {
42 strategy = new QgsNullCacheStrategy();
44 QStringLiteral( "Initializing 'off' cache strategy" ),
45 QStringLiteral( "Server" ), Qgis::MessageLevel::Info
46 );
47 }
48 else
49 {
50 strategy = new QgsFileSystemCacheStrategy();
52 QStringLiteral( "Initializing 'filesystem' cache strategy" ),
53 QStringLiteral( "Server" ), Qgis::MessageLevel::Info
54 );
55 }
56
57 return strategy;
58}
59
60
62{
63 if ( sInstance )
64 {
66 QStringLiteral( "Project's cache is already initialized" ),
67 QStringLiteral( "Server" ), Qgis::MessageLevel::Warning
68 );
69 return;
70 }
71
72 sInstance = new QgsConfigCache( getStrategyFromSettings( settings ) );
73}
74
76{
77 if ( !sInstance )
78 {
79 qFatal( "QgsConfigCache must be initialized before accessing QgsConfigCache instance." );
80 Q_ASSERT( false );
81 }
82 return sInstance;
83}
84
87{
88 mXmlDocumentCache.setMaxCost( settings->projectCacheSize() );
89 mProjectCache.setMaxCost( settings->projectCacheSize() );
90}
91
93 : mStrategy( strategy )
94{
95 mStrategy->attach( this );
96}
97
98QgsConfigCache::QgsConfigCache()
100{
101}
102
103const QgsProject *QgsConfigCache::project( const QString &path, const QgsServerSettings *settings )
104{
105 if ( !mProjectCache[path] )
106 {
107 // disable the project style database -- this incurs unwanted cost and is not required
108 auto prj = std::make_unique<QgsProject>( nullptr, Qgis::ProjectCapabilities() );
109
110 // This is required by virtual layers that call QgsProject::instance() inside the constructor :(
111 QgsProject::setInstance( prj.get() );
112
113 QgsStoreBadLayerInfo *badLayerHandler = new QgsStoreBadLayerInfo();
114 prj->setBadLayerHandler( badLayerHandler );
115
116 // Always skip original styles storage
120 if ( settings )
121 {
122 // Activate trust layer metadata flag
123 if ( settings->trustLayerMetadata() )
124 {
126 }
127 // Activate force layer read only flag
128 if ( settings->forceReadOnlyLayers() )
129 {
131 }
132 // Activate don't load layouts flag
133 if ( settings->getPrintDisabled() )
134 {
136 }
137 }
138
139 if ( prj->read( path, readFlags ) )
140 {
141 if ( !badLayerHandler->badLayers().isEmpty() )
142 {
143 // if bad layers are not restricted layers so service failed
144 QStringList unrestrictedBadLayers;
145 // test bad layers through restrictedlayers
146 const QStringList badLayerIds = badLayerHandler->badLayers();
147 const QMap<QString, QString> badLayerNames = badLayerHandler->badLayerNames();
148 const QStringList resctrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *prj );
149 for ( const QString &badLayerId : badLayerIds )
150 {
151 // if this bad layer is in restricted layers
152 // it doesn't need to be added to unrestricted bad layers
153 if ( badLayerNames.contains( badLayerId ) && resctrictedLayers.contains( badLayerNames.value( badLayerId ) ) )
154 {
155 continue;
156 }
157 unrestrictedBadLayers.append( badLayerId );
158 }
159 if ( !unrestrictedBadLayers.isEmpty() )
160 {
161 // This is a critical error unless QGIS_SERVER_IGNORE_BAD_LAYERS is set to TRUE
162 if ( !settings || !settings->ignoreBadLayers() )
163 {
165 QStringLiteral( "Error, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QLatin1String( ", " ) ), path ),
166 QStringLiteral( "Server" ), Qgis::MessageLevel::Critical
167 );
168 throw QgsServerException( QStringLiteral( "Layer(s) not valid" ) );
169 }
170 else
171 {
173 QStringLiteral( "Warning, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QLatin1String( ", " ) ), path ),
174 QStringLiteral( "Server" ), Qgis::MessageLevel::Warning
175 );
176 }
177 }
178 }
179 cacheProject( path, prj.release() );
180 }
181 else
182 {
184 QStringLiteral( "Error when loading project file '%1': %2 " ).arg( path, prj->error() ),
185 QStringLiteral( "Server" ), Qgis::MessageLevel::Critical
186 );
187 }
188 }
189
190 auto entry = mProjectCache[path];
191 return entry ? entry->second.get() : nullptr;
192}
193
194QList<QgsProject *> QgsConfigCache::projects() const
195{
196 QList<QgsProject *> projects;
197
198 const auto constKeys { mProjectCache.keys() };
199 for ( const auto &path : std::as_const( constKeys ) )
200 {
201 projects << mProjectCache[path]->second.get();
202 }
203
204 return projects;
205}
206
207QDomDocument *QgsConfigCache::xmlDocument( const QString &filePath )
208{
209 //first open file
210 QFile configFile( filePath );
211 if ( !configFile.exists() )
212 {
213 QgsMessageLog::logMessage( "Error, configuration file '" + filePath + "' does not exist", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
214 return nullptr;
215 }
216
217 if ( !configFile.open( QIODevice::ReadOnly ) )
218 {
219 QgsMessageLog::logMessage( "Error, cannot open configuration file '" + filePath + "'", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
220 return nullptr;
221 }
222
223 // first get cache
224 QDomDocument *xmlDoc = mXmlDocumentCache.object( filePath );
225 if ( !xmlDoc )
226 {
227 //then create xml document
228 xmlDoc = new QDomDocument();
229 QString errorMsg;
230 int line, column;
231 if ( !xmlDoc->setContent( &configFile, true, &errorMsg, &line, &column ) )
232 {
233 QgsMessageLog::logMessage( "Error parsing file '" + filePath + QStringLiteral( "': parse error %1 at row %2, column %3" ).arg( errorMsg ).arg( line ).arg( column ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
234 delete xmlDoc;
235 return nullptr;
236 }
237 mXmlDocumentCache.insert( filePath, xmlDoc );
238 xmlDoc = mXmlDocumentCache.object( filePath );
239 Q_ASSERT( xmlDoc );
240 }
241 return xmlDoc;
242}
243
244
245void QgsConfigCache::cacheProject( const QString &path, QgsProject *project )
246{
247 mProjectCache.insert( path, new std::pair<QDateTime, std::unique_ptr<QgsProject>>( project->lastModified(), std::unique_ptr<QgsProject>( project ) ) );
248
249 mStrategy->entryInserted( path );
250}
251
252void QgsConfigCache::removeEntry( const QString &path )
253{
254 mProjectCache.remove( path );
255
256 //xml document must be removed last, as other config cache destructors may require it
257 mXmlDocumentCache.remove( path );
258
259 mStrategy->entryRemoved( path );
260
261 emit projectRemovedFromCache( path );
262}
263
264// slots
265
266void QgsConfigCache::removeChangedEntry( const QString &path )
267{
268 removeEntry( path );
269}
270
271
273{
274 // QCache::keys returns a QList so it is safe
275 // to mutate while iterating
276 const auto constKeys { mProjectCache.keys() };
277 for ( const auto &path : std::as_const( constKeys ) )
278 {
279 const auto entry = mProjectCache[path];
280 if ( entry && entry->first < entry->second->lastModified() )
281 {
282 removeEntry( path );
283 }
284 }
285}
286
287// File system invalidation strategy
288
289
293
295{
296 QObject::connect( &mFileSystemWatcher, &QFileSystemWatcher::fileChanged, cache, &QgsConfigCache::removeChangedEntry );
297}
298
300{
301 mFileSystemWatcher.removePath( path );
302}
303
305{
306 mFileSystemWatcher.addPath( path );
307}
308
309// Periodic invalidation strategy
310
312 : mInterval( interval )
313{
314}
315
317{
318 QObject::connect( &mTimer, &QTimer::timeout, cache, &QgsConfigCache::removeChangedEntries );
319}
320
321
322void QgsPeriodicCacheStrategy::entryRemoved( const QString &path )
323{
324 Q_UNUSED( path )
325 // No-op
326}
327
329{
330 Q_UNUSED( path )
331 if ( !mTimer.isActive() )
332 {
333 mTimer.start( mInterval );
334 }
335}
336
338{
339 if ( mTimer.isActive() )
340 {
341 // Restart timer
342 mTimer.start( msec );
343 }
344}
345
346
347// Null strategy
348
350{
351 Q_UNUSED( cache )
352}
353
354void QgsNullCacheStrategy::entryRemoved( const QString &path )
355{
356 Q_UNUSED( path )
357}
358
359void QgsNullCacheStrategy::entryInserted( const QString &path )
360{
361 Q_UNUSED( path )
362}
@ DontLoad3DViews
Skip loading 3D views.
@ DontStoreOriginalStyles
Skip the initial XML style storage for layers. Useful for minimising project load times in non-intera...
@ ForceReadOnlyLayers
Open layers in a read-only mode.
@ TrustLayerMetadata
Trust layer metadata. Improves project read time. Do not use it if layers' extent is not fixed during...
@ DontUpgradeAnnotations
Don't upgrade old annotation items to QgsAnnotationItem.
@ DontLoadLayouts
Don't load print layouts. Improves project read time if layouts are not required, and allows projects...
QFlags< ProjectCapability > ProjectCapabilities
Flags which control project capabilities.
Definition qgis.h:4194
QFlags< ProjectReadFlag > ProjectReadFlags
Project load flags.
Definition qgis.h:4172
@ Warning
Warning message.
Definition qgis.h:156
@ Critical
Critical/error message.
Definition qgis.h:157
@ Info
Information message.
Definition qgis.h:155
Abstract base class for implementing cache invalidation strategy.
Cache for server configuration.
QList< QgsProject * > projects() const
Returns projects currently in cache.
QgsConfigCache(QgsServerSettings *settings)
Initialize from settings.
void removeChangedEntry(const QString &path)
Remove cache entry.
void removeEntry(const QString &path)
Removes an entry from cache.
void removeChangedEntries()
Remove all changed cache entries.
static QgsConfigCache * instance()
Returns the current instance.
static void initialize(QgsServerSettings *settings)
Initialize from settings.
void projectRemovedFromCache(const QString &path)
Emitted whenever a project is removed from the cache.
const QgsProject * project(const QString &path, const QgsServerSettings *settings=nullptr)
If the project is not cached yet, then the project is read from the path.
File system cache strategy for server configuration.
void entryInserted(const QString &path) override
Called when an entry is inserted.
QgsFileSystemCacheStrategy()
Creates a new filesystem strategy.
void entryRemoved(const QString &path) override
Called when an entry is removed from cache.
void attach(QgsConfigCache *cache) override
Attach cache to this strategy.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
Null system cache strategy for server configuration, completely disable cache invalidation invalidati...
void entryInserted(const QString &path) override
Called when an entry is inserted.
void entryRemoved(const QString &path) override
Called when an entry is removed from cache.
void attach(QgsConfigCache *owner) override
Attaches cache to this strategy.
Periodic system cache strategy for server configuration.
void entryInserted(const QString &path) override
Called when an entry is inserted.
void attach(QgsConfigCache *owner) override
Attaches cache to this strategy.
QgsPeriodicCacheStrategy(int interval=3000)
Creates a new periodic strategy.
void entryRemoved(const QString &path) override
Called when an entry is removed from cache.
void setCheckInterval(int msec)
Sets the invalidation check interval for PeriodicStrategy.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:107
static void setInstance(QgsProject *project)
Set the current project singleton instance to project.
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
Exception base class for server exceptions.
static QStringList wmsRestrictedLayers(const QgsProject &project)
Returns the restricted layer name list.
Provides a way to retrieve settings by prioritizing according to environment variables,...
bool getPrintDisabled() const
Returns true if WMS GetPrint request is disabled and the project's reading flag QgsProject::ReadFlag:...
int projectCacheSize() const
Returns the projects cache size The default value is 100, the value can be changed by setting the env...
int projectCacheCheckInterval() const
Returns the config cache check interval (in ms) for the 'periodic' strategy.
bool forceReadOnlyLayers() const
Returns true if the reading flag force layer read only is activated.
bool ignoreBadLayers() const
Returns true if the bad layers are ignored and false when the presence of a bad layers invalidates th...
QString projectCacheStrategy() const
Returns the project's cache strategy The default value is 'filesystem', the value can be changed by s...
bool trustLayerMetadata() const
Returns true if the reading flag trust layer metadata is activated.
Stores layer IDs for bad (broken) layers.
QMap< QString, QString > badLayerNames() const
Returns names of bad layers with ids.
QStringList badLayers() const
badLayers
QgsAbstractCacheStrategy * getStrategyFromSettings(QgsServerSettings *settings)