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