QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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
111 Qgis::ProjectReadFlags readFlags = Qgis::ProjectReadFlag::DontStoreOriginalStyles
112 | Qgis::ProjectReadFlag::DontLoad3DViews;
113 if ( settings )
114 {
115 // Activate trust layer metadata flag
116 if ( settings->trustLayerMetadata() )
117 {
118 readFlags |= Qgis::ProjectReadFlag::TrustLayerMetadata;
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 {
128 readFlags |= Qgis::ProjectReadFlag::DontLoadLayouts;
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
185QDomDocument *QgsConfigCache::xmlDocument( const QString &filePath )
186{
187 //first open file
188 QFile configFile( filePath );
189 if ( !configFile.exists() )
190 {
191 QgsMessageLog::logMessage( "Error, configuration file '" + filePath + "' does not exist", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
192 return nullptr;
193 }
194
195 if ( !configFile.open( QIODevice::ReadOnly ) )
196 {
197 QgsMessageLog::logMessage( "Error, cannot open configuration file '" + filePath + "'", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
198 return nullptr;
199 }
200
201 // first get cache
202 QDomDocument *xmlDoc = mXmlDocumentCache.object( filePath );
203 if ( !xmlDoc )
204 {
205 //then create xml document
206 xmlDoc = new QDomDocument();
207 QString errorMsg;
208 int line, column;
209 if ( !xmlDoc->setContent( &configFile, true, &errorMsg, &line, &column ) )
210 {
211 QgsMessageLog::logMessage( "Error parsing file '" + filePath +
212 QStringLiteral( "': parse error %1 at row %2, column %3" ).arg( errorMsg ).arg( line ).arg( column ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
213 delete xmlDoc;
214 return nullptr;
215 }
216 mXmlDocumentCache.insert( filePath, xmlDoc );
217 xmlDoc = mXmlDocumentCache.object( filePath );
218 Q_ASSERT( xmlDoc );
219 }
220 return xmlDoc;
221}
222
223
224void QgsConfigCache::cacheProject( const QString &path, QgsProject *project )
225{
226 mProjectCache.insert( path, new std::pair<QDateTime, std::unique_ptr<QgsProject> >( project->lastModified(), std::unique_ptr<QgsProject>( project ) ) );
227
228 mStrategy->entryInserted( path );
229}
230
231void QgsConfigCache::removeEntry( const QString &path )
232{
233 mProjectCache.remove( path );
234
235 //xml document must be removed last, as other config cache destructors may require it
236 mXmlDocumentCache.remove( path );
237
238 mStrategy->entryRemoved( path );
239}
240
241// slots
242
243void QgsConfigCache::removeChangedEntry( const QString &path )
244{
245 removeEntry( path );
246}
247
248
250{
251 // QCache::keys returns a QList so it is safe
252 // to mutate while iterating
253 const auto constKeys { mProjectCache.keys() };
254 for ( const auto &path : std::as_const( constKeys ) )
255 {
256 const auto entry = mProjectCache[ path ];
257 if ( entry && entry->first < entry->second->lastModified() )
258 {
259 removeEntry( path );
260 }
261 }
262}
263
264// File system invalidation strategy
265
266
267
269{
270}
271
273{
274 QObject::connect( &mFileSystemWatcher, &QFileSystemWatcher::fileChanged, cache, &QgsConfigCache::removeChangedEntry );
275}
276
278{
279 mFileSystemWatcher.removePath( path );
280}
281
283{
284 mFileSystemWatcher.addPath( path );
285}
286
287// Periodic invalidation strategy
288
290 : mInterval( interval )
291{
292}
293
295{
296 QObject::connect( &mTimer, &QTimer::timeout, cache, &QgsConfigCache::removeChangedEntries );
297}
298
299
300
301void QgsPeriodicCacheStrategy::entryRemoved( const QString &path )
302{
303 Q_UNUSED( path )
304 // No-op
305}
306
308{
309 Q_UNUSED( path )
310 if ( !mTimer.isActive() )
311 {
312 mTimer.start( mInterval );
313 }
314}
315
317{
318 if ( mTimer.isActive() )
319 {
320 // Restart timer
321 mTimer.start( msec );
322 }
323}
324
325
326// Null strategy
327
329{
330 Q_UNUSED( cache )
331}
332
333void QgsNullCacheStrategy::entryRemoved( const QString &path )
334{
335 Q_UNUSED( path )
336}
337
338void QgsNullCacheStrategy::entryInserted( const QString &path )
339{
340 Q_UNUSED( path )
341}
@ ForceReadOnlyLayers
Open layers in a read-only mode. (since QGIS 3.28)
Abstract base class for implementing cache invalidation strategy.
Cache for server configuration.
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:104
static void setInstance(QgsProject *project)
Set the current project singleton instance to project.
Definition: qgsproject.cpp:471
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
Definition: qgsproject.cpp:795
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 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)