QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
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
19#include "qgsmessagelog.h"
20#include "qgsserverexception.h"
23#include "qgsvectorlayer.h"
24
25#include <QFile>
26
27#include "moc_qgsconfigcache.cpp"
28
29QgsConfigCache *QgsConfigCache::sInstance = nullptr;
30
31
33{
35 if ( settings && settings->projectCacheStrategy() == QLatin1String( "periodic" ) )
36 {
37 strategy = new QgsPeriodicCacheStrategy( settings->projectCacheCheckInterval() );
39 QStringLiteral( "Initializing 'periodic' cache strategy" ),
40 QStringLiteral( "Server" ), Qgis::MessageLevel::Info
41 );
42 }
43 else if ( settings && settings->projectCacheStrategy() == QLatin1String( "off" ) )
44 {
45 strategy = new QgsNullCacheStrategy();
47 QStringLiteral( "Initializing 'off' cache strategy" ),
48 QStringLiteral( "Server" ), Qgis::MessageLevel::Info
49 );
50 }
51 else
52 {
53 strategy = new QgsFileSystemCacheStrategy();
55 QStringLiteral( "Initializing 'filesystem' cache strategy" ),
56 QStringLiteral( "Server" ), Qgis::MessageLevel::Info
57 );
58 }
59
60 return strategy;
61}
62
63
65{
66 if ( sInstance )
67 {
69 QStringLiteral( "Project's cache is already initialized" ),
70 QStringLiteral( "Server" ), Qgis::MessageLevel::Warning
71 );
72 return;
73 }
74
75 sInstance = new QgsConfigCache( getStrategyFromSettings( settings ) );
76}
77
79{
80 if ( !sInstance )
81 {
82 qFatal( "QgsConfigCache must be initialized before accessing QgsConfigCache instance." );
83 Q_ASSERT( false );
84 }
85 return sInstance;
86}
87
90{
91 mXmlDocumentCache.setMaxCost( settings->projectCacheSize() );
92 mProjectCache.setMaxCost( settings->projectCacheSize() );
93}
94
96 : mStrategy( strategy )
97{
98 mStrategy->attach( this );
99}
100
103{
104}
105
106const QgsProject *QgsConfigCache::project( const QString &path, const QgsServerSettings *settings )
107{
108 if ( !mProjectCache[path] )
109 {
110 // disable the project style database -- this incurs unwanted cost and is not required
111 auto prj = std::make_unique<QgsProject>( nullptr, Qgis::ProjectCapabilities() );
112
113 // This is required by virtual layers that call QgsProject::instance() inside the constructor :(
114 QgsProject::setInstance( prj.get() );
115
116 QgsStoreBadLayerInfo *badLayerHandler = new QgsStoreBadLayerInfo();
117 prj->setBadLayerHandler( badLayerHandler );
118
119 // Always skip original styles storage
123 if ( settings )
124 {
125 // Activate trust layer metadata flag
126 if ( settings->trustLayerMetadata() )
127 {
129 }
130 // Activate force layer read only flag
131 if ( settings->forceReadOnlyLayers() )
132 {
134 }
135 // Activate don't load layouts flag
136 if ( settings->getPrintDisabled() )
137 {
139 }
140 }
141
142 if ( prj->read( path, readFlags ) )
143 {
144 if ( !badLayerHandler->badLayers().isEmpty() )
145 {
146 // if bad layers are not restricted layers so service failed
147 QStringList unrestrictedBadLayers;
148 // test bad layers through restrictedlayers
149 const QStringList badLayerIds = badLayerHandler->badLayers();
150 const QMap<QString, QString> badLayerNames = badLayerHandler->badLayerNames();
151 const QStringList restrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *prj );
152 for ( const QString &badLayerId : badLayerIds )
153 {
154 // if this bad layer is in restricted layers
155 // it doesn't need to be added to unrestricted bad layers
156 if ( badLayerNames.contains( badLayerId ) && restrictedLayers.contains( badLayerNames.value( badLayerId ) ) )
157 {
158 continue;
159 }
160 unrestrictedBadLayers.append( badLayerId );
161 }
162 if ( !unrestrictedBadLayers.isEmpty() )
163 {
164 // This is a critical error unless QGIS_SERVER_IGNORE_BAD_LAYERS is set to TRUE
165 if ( !settings || !settings->ignoreBadLayers() )
166 {
168 QStringLiteral( "Error, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QLatin1String( ", " ) ), path ),
169 QStringLiteral( "Server" ), Qgis::MessageLevel::Critical
170 );
171 throw QgsServerException( QStringLiteral( "Layer(s) not valid" ) );
172 }
173 else
174 {
176 QStringLiteral( "Warning, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QLatin1String( ", " ) ), path ),
177 QStringLiteral( "Server" ), Qgis::MessageLevel::Warning
178 );
179 }
180 }
181 }
182 cacheProject( path, prj.release() );
183 }
184 else
185 {
187 QStringLiteral( "Error when loading project file '%1': %2 " ).arg( path, prj->error() ),
188 QStringLiteral( "Server" ), Qgis::MessageLevel::Critical
189 );
190 }
191 }
192
193 auto entry = mProjectCache[path];
194 if ( !entry )
195 {
196 return nullptr;
197 }
198
199 //Try to reload data sources of invalid layers
200 if ( ( settings && settings->retryBadLayers() ) && ( entry->second->validCount() != entry->second->count() ) )
201 {
202 for ( const auto &l : entry->second->mapLayers() )
203 {
204 if ( !l->isValid() )
205 {
206 QString subsetString;
207 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( l );
208 if ( vlayer )
209 {
210 subsetString = vlayer->subsetString();
211 }
213 l->setDataSource( l->source(), l->name(), l->providerType(), options );
214 if ( vlayer && !subsetString.isEmpty() )
215 {
216 vlayer->setSubsetString( subsetString );
217 }
218 }
219 }
220 }
221
222 return entry->second.get();
223}
224
225QList<QgsProject *> QgsConfigCache::projects() const
226{
227 QList<QgsProject *> projects;
228
229 const auto constKeys { mProjectCache.keys() };
230 for ( const auto &path : std::as_const( constKeys ) )
231 {
232 projects << mProjectCache[path]->second.get();
233 }
234
235 return projects;
236}
237
238QDomDocument *QgsConfigCache::xmlDocument( const QString &filePath )
239{
240 //first open file
241 QFile configFile( filePath );
242 if ( !configFile.exists() )
243 {
244 QgsMessageLog::logMessage( "Error, configuration file '" + filePath + "' does not exist", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
245 return nullptr;
246 }
247
248 if ( !configFile.open( QIODevice::ReadOnly ) )
249 {
250 QgsMessageLog::logMessage( "Error, cannot open configuration file '" + filePath + "'", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
251 return nullptr;
252 }
253
254 // first get cache
255 QDomDocument *xmlDoc = mXmlDocumentCache.object( filePath );
256 if ( !xmlDoc )
257 {
258 //then create xml document
259 xmlDoc = new QDomDocument();
260 QString errorMsg;
261 int line, column;
262 if ( !xmlDoc->setContent( &configFile, true, &errorMsg, &line, &column ) )
263 {
264 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 );
265 delete xmlDoc;
266 return nullptr;
267 }
268 mXmlDocumentCache.insert( filePath, xmlDoc );
269 xmlDoc = mXmlDocumentCache.object( filePath );
270 Q_ASSERT( xmlDoc );
271 }
272 return xmlDoc;
273}
274
275
276void QgsConfigCache::cacheProject( const QString &path, QgsProject *project )
277{
278 mProjectCache.insert( path, new std::pair<QDateTime, std::unique_ptr<QgsProject>>( project->lastModified(), std::unique_ptr<QgsProject>( project ) ) );
279
280 mStrategy->entryInserted( path );
281}
282
283void QgsConfigCache::removeEntry( const QString &path )
284{
285 mProjectCache.remove( path );
286
287 //xml document must be removed last, as other config cache destructors may require it
288 mXmlDocumentCache.remove( path );
289
290 mStrategy->entryRemoved( path );
291
292 emit projectRemovedFromCache( path );
293}
294
295// slots
296
297void QgsConfigCache::removeChangedEntry( const QString &path )
298{
299 removeEntry( path );
300}
301
302
304{
305 // QCache::keys returns a QList so it is safe
306 // to mutate while iterating
307 const auto constKeys { mProjectCache.keys() };
308 for ( const auto &path : std::as_const( constKeys ) )
309 {
310 const auto entry = mProjectCache[path];
311 if ( entry && entry->first < entry->second->lastModified() )
312 {
313 removeEntry( path );
314 }
315 }
316}
317
318// File system invalidation strategy
319
320
324
326{
327 QObject::connect( &mFileSystemWatcher, &QFileSystemWatcher::fileChanged, cache, &QgsConfigCache::removeChangedEntry );
328}
329
331{
332 mFileSystemWatcher.removePath( path );
333}
334
336{
337 mFileSystemWatcher.addPath( path );
338}
339
340// Periodic invalidation strategy
341
343 : mInterval( interval )
344{
345}
346
348{
349 QObject::connect( &mTimer, &QTimer::timeout, cache, &QgsConfigCache::removeChangedEntries );
350}
351
352
353void QgsPeriodicCacheStrategy::entryRemoved( const QString &path )
354{
355 Q_UNUSED( path )
356 // No-op
357}
358
360{
361 Q_UNUSED( path )
362 if ( !mTimer.isActive() )
363 {
364 mTimer.start( mInterval );
365 }
366}
367
369{
370 if ( mTimer.isActive() )
371 {
372 // Restart timer
373 mTimer.start( msec );
374 }
375}
376
377
378// Null strategy
379
381{
382 Q_UNUSED( cache )
383}
384
385void QgsNullCacheStrategy::entryRemoved( const QString &path )
386{
387 Q_UNUSED( path )
388}
389
390void QgsNullCacheStrategy::entryInserted( const QString &path )
391{
392 Q_UNUSED( path )
393}
@ DontLoad3DViews
Skip loading 3D views.
Definition qgis.h:4307
@ DontStoreOriginalStyles
Skip the initial XML style storage for layers. Useful for minimising project load times in non-intera...
Definition qgis.h:4306
@ ForceReadOnlyLayers
Open layers in a read-only mode.
Definition qgis.h:4309
@ TrustLayerMetadata
Trust layer metadata. Improves project read time. Do not use it if layers' extent is not fixed during...
Definition qgis.h:4305
@ DontUpgradeAnnotations
Don't upgrade old annotation items to QgsAnnotationItem.
Definition qgis.h:4310
@ DontLoadLayouts
Don't load print layouts. Improves project read time if layouts are not required, and allows projects...
Definition qgis.h:4304
QFlags< ProjectCapability > ProjectCapabilities
Flags which control project capabilities.
Definition qgis.h:4343
QFlags< ProjectReadFlag > ProjectReadFlags
Project load flags.
Definition qgis.h:4321
@ Warning
Warning message.
Definition qgis.h:158
@ Critical
Critical/error message.
Definition qgis.h:159
@ Info
Information message.
Definition qgis.h:157
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:109
static void setInstance(QgsProject *project)
Set the current project singleton instance to project.
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 retryBadLayers() const
Returns true if bad layers should be re-checked after the project has been cached.
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
Represents a vector layer which manages a vector based dataset.
virtual bool setSubsetString(const QString &subset)
Sets the string (typically sql) used to define a subset of the layer.
QgsAbstractCacheStrategy * getStrategyFromSettings(QgsServerSettings *settings)
Setting options for creating vector data providers.