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