QGIS API Documentation 3.39.0-Master (d85f3c2a281)
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#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
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
114 if ( settings )
115 {
116 // Activate trust layer metadata flag
117 if ( settings->trustLayerMetadata() )
118 {
120 }
121 // Activate force layer read only flag
122 if ( settings->forceReadOnlyLayers() )
123 {
125 }
126 // Activate don't load layouts flag
127 if ( settings->getPrintDisabled() )
128 {
130 }
131 }
132
133 if ( prj->read( path, readFlags ) )
134 {
135 if ( !badLayerHandler->badLayers().isEmpty() )
136 {
137 // if bad layers are not restricted layers so service failed
138 QStringList unrestrictedBadLayers;
139 // test bad layers through restrictedlayers
140 const QStringList badLayerIds = badLayerHandler->badLayers();
141 const QMap<QString, QString> badLayerNames = badLayerHandler->badLayerNames();
142 const QStringList resctrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *prj );
143 for ( const QString &badLayerId : badLayerIds )
144 {
145 // if this bad layer is in restricted layers
146 // it doesn't need to be added to unrestricted bad layers
147 if ( badLayerNames.contains( badLayerId ) &&
148 resctrictedLayers.contains( badLayerNames.value( badLayerId ) ) )
149 {
150 continue;
151 }
152 unrestrictedBadLayers.append( badLayerId );
153 }
154 if ( !unrestrictedBadLayers.isEmpty() )
155 {
156 // This is a critical error unless QGIS_SERVER_IGNORE_BAD_LAYERS is set to TRUE
157 if ( ! settings || ! settings->ignoreBadLayers() )
158 {
160 QStringLiteral( "Error, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QLatin1String( ", " ) ), path ),
161 QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
162 throw QgsServerException( QStringLiteral( "Layer(s) not valid" ) );
163 }
164 else
165 {
167 QStringLiteral( "Warning, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QLatin1String( ", " ) ), path ),
168 QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
169 }
170 }
171 }
172 cacheProject( path, prj.release() );
173 }
174 else
175 {
177 QStringLiteral( "Error when loading project file '%1': %2 " ).arg( path, prj->error() ),
178 QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
179 }
180 }
181
182 auto entry = mProjectCache[ path ];
183 return entry ? entry->second.get() : nullptr;
184}
185
186QList<QgsProject *> QgsConfigCache::projects() const
187{
188 QList<QgsProject *> projects;
189
190 const auto constKeys { mProjectCache.keys() };
191 for ( const auto &path : std::as_const( constKeys ) )
192 {
193 projects << mProjectCache[path]->second.get();
194 }
195
196 return projects;
197}
198
199QDomDocument *QgsConfigCache::xmlDocument( const QString &filePath )
200{
201 //first open file
202 QFile configFile( filePath );
203 if ( !configFile.exists() )
204 {
205 QgsMessageLog::logMessage( "Error, configuration file '" + filePath + "' does not exist", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
206 return nullptr;
207 }
208
209 if ( !configFile.open( QIODevice::ReadOnly ) )
210 {
211 QgsMessageLog::logMessage( "Error, cannot open configuration file '" + filePath + "'", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
212 return nullptr;
213 }
214
215 // first get cache
216 QDomDocument *xmlDoc = mXmlDocumentCache.object( filePath );
217 if ( !xmlDoc )
218 {
219 //then create xml document
220 xmlDoc = new QDomDocument();
221 QString errorMsg;
222 int line, column;
223 if ( !xmlDoc->setContent( &configFile, true, &errorMsg, &line, &column ) )
224 {
225 QgsMessageLog::logMessage( "Error parsing file '" + filePath +
226 QStringLiteral( "': parse error %1 at row %2, column %3" ).arg( errorMsg ).arg( line ).arg( column ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
227 delete xmlDoc;
228 return nullptr;
229 }
230 mXmlDocumentCache.insert( filePath, xmlDoc );
231 xmlDoc = mXmlDocumentCache.object( filePath );
232 Q_ASSERT( xmlDoc );
233 }
234 return xmlDoc;
235}
236
237
238void QgsConfigCache::cacheProject( const QString &path, QgsProject *project )
239{
240 mProjectCache.insert( path, new std::pair<QDateTime, std::unique_ptr<QgsProject> >( project->lastModified(), std::unique_ptr<QgsProject>( project ) ) );
241
242 mStrategy->entryInserted( path );
243}
244
245void QgsConfigCache::removeEntry( const QString &path )
246{
247 mProjectCache.remove( path );
248
249 //xml document must be removed last, as other config cache destructors may require it
250 mXmlDocumentCache.remove( path );
251
252 mStrategy->entryRemoved( path );
253
254 emit projectRemovedFromCache( path );
255}
256
257// slots
258
259void QgsConfigCache::removeChangedEntry( const QString &path )
260{
261 removeEntry( path );
262}
263
264
266{
267 // QCache::keys returns a QList so it is safe
268 // to mutate while iterating
269 const auto constKeys { mProjectCache.keys() };
270 for ( const auto &path : std::as_const( constKeys ) )
271 {
272 const auto entry = mProjectCache[ path ];
273 if ( entry && entry->first < entry->second->lastModified() )
274 {
275 removeEntry( path );
276 }
277 }
278}
279
280// File system invalidation strategy
281
282
283
287
289{
290 QObject::connect( &mFileSystemWatcher, &QFileSystemWatcher::fileChanged, cache, &QgsConfigCache::removeChangedEntry );
291}
292
294{
295 mFileSystemWatcher.removePath( path );
296}
297
299{
300 mFileSystemWatcher.addPath( path );
301}
302
303// Periodic invalidation strategy
304
306 : mInterval( interval )
307{
308}
309
311{
312 QObject::connect( &mTimer, &QTimer::timeout, cache, &QgsConfigCache::removeChangedEntries );
313}
314
315
316
317void QgsPeriodicCacheStrategy::entryRemoved( const QString &path )
318{
319 Q_UNUSED( path )
320 // No-op
321}
322
324{
325 Q_UNUSED( path )
326 if ( !mTimer.isActive() )
327 {
328 mTimer.start( mInterval );
329 }
330}
331
333{
334 if ( mTimer.isActive() )
335 {
336 // Restart timer
337 mTimer.start( msec );
338 }
339}
340
341
342// Null strategy
343
345{
346 Q_UNUSED( cache )
347}
348
349void QgsNullCacheStrategy::entryRemoved( const QString &path )
350{
351 Q_UNUSED( path )
352}
353
354void QgsNullCacheStrategy::entryInserted( const QString &path )
355{
356 Q_UNUSED( path )
357}
@ 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:4017
QFlags< ProjectReadFlag > ProjectReadFlags
Project load flags.
Definition qgis.h:3995
@ 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)
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.
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)