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