QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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 "qgsmessagelog.h"
19#include "qgsserverexception.h"
22
23#include <QFile>
24
25QgsConfigCache *QgsConfigCache::sInstance = nullptr;
26
27
29{
31 if ( settings && settings->projectCacheStrategy() == QLatin1String( "periodic" ) )
32 {
33 strategy = new QgsPeriodicCacheStrategy( settings->projectCacheCheckInterval() );
35 QStringLiteral( "Initializing 'periodic' cache strategy" ),
36 QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
37 }
38 else if ( settings && settings->projectCacheStrategy() == QLatin1String( "off" ) )
39 {
40 strategy = new QgsNullCacheStrategy();
42 QStringLiteral( "Initializing 'off' cache strategy" ),
43 QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
44 }
45 else
46 {
47 strategy = new QgsFileSystemCacheStrategy();
49 QStringLiteral( "Initializing 'filesystem' cache strategy" ),
50 QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
51 }
52
53 return strategy;
54}
55
56
58{
59 if ( sInstance )
60 {
62 QStringLiteral( "Project's cache is already initialized" ),
63 QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
64 return;
65 }
66
67 sInstance = new QgsConfigCache( getStrategyFromSettings( settings ) );
68}
69
71{
72 if ( !sInstance )
73 {
74 qFatal( "QgsConfigCache must be initialized before accessing QgsConfigCache instance." );
75 Q_ASSERT( false );
76 }
77 return sInstance;
78}
79
82{
83
84}
85
87 : mStrategy( strategy )
88{
89 mStrategy->attach( this );
90}
91
92QgsConfigCache::QgsConfigCache() : QgsConfigCache( new QgsFileSystemCacheStrategy() )
93{
94
95}
96
97const QgsProject *QgsConfigCache::project( const QString &path, const QgsServerSettings *settings )
98{
99 if ( !mProjectCache[ path ] )
100 {
101 // disable the project style database -- this incurs unwanted cost and is not required
102 std::unique_ptr<QgsProject> prj( new QgsProject( nullptr, Qgis::ProjectCapabilities() ) );
103
104 // This is required by virtual layers that call QgsProject::instance() inside the constructor :(
105 QgsProject::setInstance( prj.get() );
106
107 QgsStoreBadLayerInfo *badLayerHandler = new QgsStoreBadLayerInfo();
108 prj->setBadLayerHandler( badLayerHandler );
109
110 // Always skip original styles storage
113 if ( settings )
114 {
115 // Activate trust layer metadata flag
116 if ( settings->trustLayerMetadata() )
117 {
119 }
120 // Activate force layer read only flag
121 if ( settings->forceReadOnlyLayers() )
122 {
124 }
125 // Activate don't load layouts flag
126 if ( settings->getPrintDisabled() )
127 {
129 }
130 }
131
132 if ( prj->read( path, readFlags ) )
133 {
134 if ( !badLayerHandler->badLayers().isEmpty() )
135 {
136 // if bad layers are not restricted layers so service failed
137 QStringList unrestrictedBadLayers;
138 // test bad layers through restrictedlayers
139 const QStringList badLayerIds = badLayerHandler->badLayers();
140 const QMap<QString, QString> badLayerNames = badLayerHandler->badLayerNames();
141 const QStringList resctrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *prj );
142 for ( const QString &badLayerId : badLayerIds )
143 {
144 // if this bad layer is in restricted layers
145 // it doesn't need to be added to unrestricted bad layers
146 if ( badLayerNames.contains( badLayerId ) &&
147 resctrictedLayers.contains( badLayerNames.value( badLayerId ) ) )
148 {
149 continue;
150 }
151 unrestrictedBadLayers.append( badLayerId );
152 }
153 if ( !unrestrictedBadLayers.isEmpty() )
154 {
155 // This is a critical error unless QGIS_SERVER_IGNORE_BAD_LAYERS is set to TRUE
156 if ( ! settings || ! settings->ignoreBadLayers() )
157 {
159 QStringLiteral( "Error, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QLatin1String( ", " ) ), path ),
160 QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
161 throw QgsServerException( QStringLiteral( "Layer(s) not valid" ) );
162 }
163 else
164 {
166 QStringLiteral( "Warning, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QLatin1String( ", " ) ), path ),
167 QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
168 }
169 }
170 }
171 cacheProject( path, prj.release() );
172 }
173 else
174 {
176 QStringLiteral( "Error when loading project file '%1': %2 " ).arg( path, prj->error() ),
177 QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
178 }
179 }
180
181 auto entry = mProjectCache[ path ];
182 return entry ? entry->second.get() : nullptr;
183}
184
185QList<QgsProject *> QgsConfigCache::projects() const
186{
187 QList<QgsProject *> projects;
188
189 const auto constKeys { mProjectCache.keys() };
190 for ( const auto &path : std::as_const( constKeys ) )
191 {
192 projects << mProjectCache[path]->second.get();
193 }
194
195 return projects;
196}
197
198QDomDocument *QgsConfigCache::xmlDocument( const QString &filePath )
199{
200 //first open file
201 QFile configFile( filePath );
202 if ( !configFile.exists() )
203 {
204 QgsMessageLog::logMessage( "Error, configuration file '" + filePath + "' does not exist", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
205 return nullptr;
206 }
207
208 if ( !configFile.open( QIODevice::ReadOnly ) )
209 {
210 QgsMessageLog::logMessage( "Error, cannot open configuration file '" + filePath + "'", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
211 return nullptr;
212 }
213
214 // first get cache
215 QDomDocument *xmlDoc = mXmlDocumentCache.object( filePath );
216 if ( !xmlDoc )
217 {
218 //then create xml document
219 xmlDoc = new QDomDocument();
220 QString errorMsg;
221 int line, column;
222 if ( !xmlDoc->setContent( &configFile, true, &errorMsg, &line, &column ) )
223 {
224 QgsMessageLog::logMessage( "Error parsing file '" + filePath +
225 QStringLiteral( "': parse error %1 at row %2, column %3" ).arg( errorMsg ).arg( line ).arg( column ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
226 delete xmlDoc;
227 return nullptr;
228 }
229 mXmlDocumentCache.insert( filePath, xmlDoc );
230 xmlDoc = mXmlDocumentCache.object( filePath );
231 Q_ASSERT( xmlDoc );
232 }
233 return xmlDoc;
234}
235
236
237void QgsConfigCache::cacheProject( const QString &path, QgsProject *project )
238{
239 mProjectCache.insert( path, new std::pair<QDateTime, std::unique_ptr<QgsProject> >( project->lastModified(), std::unique_ptr<QgsProject>( project ) ) );
240
241 mStrategy->entryInserted( path );
242}
243
244void QgsConfigCache::removeEntry( const QString &path )
245{
246 mProjectCache.remove( path );
247
248 //xml document must be removed last, as other config cache destructors may require it
249 mXmlDocumentCache.remove( path );
250
251 mStrategy->entryRemoved( path );
252}
253
254// slots
255
256void QgsConfigCache::removeChangedEntry( const QString &path )
257{
258 removeEntry( path );
259}
260
261
263{
264 // QCache::keys returns a QList so it is safe
265 // to mutate while iterating
266 const auto constKeys { mProjectCache.keys() };
267 for ( const auto &path : std::as_const( constKeys ) )
268 {
269 const auto entry = mProjectCache[ path ];
270 if ( entry && entry->first < entry->second->lastModified() )
271 {
272 removeEntry( path );
273 }
274 }
275}
276
277// File system invalidation strategy
278
279
280
282{
283}
284
286{
287 QObject::connect( &mFileSystemWatcher, &QFileSystemWatcher::fileChanged, cache, &QgsConfigCache::removeChangedEntry );
288}
289
291{
292 mFileSystemWatcher.removePath( path );
293}
294
296{
297 mFileSystemWatcher.addPath( path );
298}
299
300// Periodic invalidation strategy
301
303 : mInterval( interval )
304{
305}
306
308{
309 QObject::connect( &mTimer, &QTimer::timeout, cache, &QgsConfigCache::removeChangedEntries );
310}
311
312
313
314void QgsPeriodicCacheStrategy::entryRemoved( const QString &path )
315{
316 Q_UNUSED( path )
317 // No-op
318}
319
321{
322 Q_UNUSED( path )
323 if ( !mTimer.isActive() )
324 {
325 mTimer.start( mInterval );
326 }
327}
328
330{
331 if ( mTimer.isActive() )
332 {
333 // Restart timer
334 mTimer.start( msec );
335 }
336}
337
338
339// Null strategy
340
342{
343 Q_UNUSED( cache )
344}
345
346void QgsNullCacheStrategy::entryRemoved( const QString &path )
347{
348 Q_UNUSED( path )
349}
350
351void QgsNullCacheStrategy::entryInserted( const QString &path )
352{
353 Q_UNUSED( path )
354}
@ DontLoad3DViews
Skip loading 3D views (since QGIS 3.26)
@ 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. (since QGIS 3.28)
@ TrustLayerMetadata
Trust layer metadata. Improves project read time. Do not use it if layers' extent is not fixed during...
@ 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:3584
QFlags< ProjectReadFlag > ProjectReadFlags
Project load flags.
Definition: qgis.h:3562
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.
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)
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.
Definition: qgsproject.cpp:475
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
Definition: qgsproject.cpp:853
Exception base class for server exceptions.
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 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 of bad layers.
QMap< QString, QString > badLayerNames() const
Returns names of bad layers with ids.
QStringList badLayers() const
badLayers
SERVER_EXPORT QStringList wmsRestrictedLayers(const QgsProject &project)
Returns the restricted layer name list.
QgsAbstractCacheStrategy * getStrategyFromSettings(QgsServerSettings *settings)