QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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
19#include "qgsmessagelog.h"
20#include "qgsserverexception.h"
23#include "qgsvectorlayer.h"
24
25#include <QFile>
26#include <QString>
27
28#include "moc_qgsconfigcache.cpp"
29
30using namespace Qt::StringLiterals;
31
32QgsConfigCache *QgsConfigCache::sInstance = nullptr;
33
34
36{
38 if ( settings && settings->projectCacheStrategy() == "periodic"_L1 )
39 {
40 strategy = new QgsPeriodicCacheStrategy( settings->projectCacheCheckInterval() );
41 QgsMessageLog::logMessage( u"Initializing 'periodic' cache strategy"_s, u"Server"_s, Qgis::MessageLevel::Info );
42 }
43 else if ( settings && settings->projectCacheStrategy() == "off"_L1 )
44 {
45 strategy = new QgsNullCacheStrategy();
46 QgsMessageLog::logMessage( u"Initializing 'off' cache strategy"_s, u"Server"_s, Qgis::MessageLevel::Info );
47 }
48 else
49 {
50 strategy = new QgsFileSystemCacheStrategy();
51 QgsMessageLog::logMessage( u"Initializing 'filesystem' cache strategy"_s, u"Server"_s, Qgis::MessageLevel::Info );
52 }
53
54 return strategy;
55}
56
57
59{
60 if ( sInstance )
61 {
62 QgsMessageLog::logMessage( u"Project's cache is already initialized"_s, u"Server"_s, Qgis::MessageLevel::Warning );
63 return;
64 }
65
66 sInstance = new QgsConfigCache( getStrategyFromSettings( settings ) );
67}
68
70{
71 if ( !sInstance )
72 {
73 qFatal( "QgsConfigCache must be initialized before accessing QgsConfigCache instance." );
74 Q_ASSERT( false );
75 }
76 return sInstance;
77}
78
81{
82 mXmlDocumentCache.setMaxCost( settings->projectCacheSize() );
83 mProjectCache.setMaxCost( settings->projectCacheSize() );
84}
85
87 : mStrategy( strategy )
88{
89 mStrategy->attach( this );
90}
91
94{}
95
96const QgsProject *QgsConfigCache::project( const QString &path, const QgsServerSettings *settings )
97{
98 if ( !mProjectCache[path] )
99 {
100 // disable the project style database -- this incurs unwanted cost and is not required
101 auto prj = std::make_unique<QgsProject>( nullptr, Qgis::ProjectCapabilities() );
102
103 // This is required by virtual layers that call QgsProject::instance() inside the constructor :(
104 QgsProject::setInstance( prj.get() );
105
106 QgsStoreBadLayerInfo *badLayerHandler = new QgsStoreBadLayerInfo();
107 prj->setBadLayerHandler( badLayerHandler );
108
109 // Always skip original styles storage
111 if ( settings )
112 {
113 // Activate trust layer metadata flag
114 if ( settings->trustLayerMetadata() )
115 {
117 }
118 // Activate force layer read only flag
119 if ( settings->forceReadOnlyLayers() )
120 {
122 }
123 // Activate don't load layouts flag
124 if ( settings->getPrintDisabled() )
125 {
127 }
128 }
129
130 if ( prj->read( path, readFlags ) )
131 {
132 if ( !badLayerHandler->badLayers().isEmpty() )
133 {
134 // if bad layers are not restricted layers so service failed
135 QStringList unrestrictedBadLayers;
136 // test bad layers through restrictedlayers
137 const QStringList badLayerIds = badLayerHandler->badLayers();
138 const QMap<QString, QString> badLayerNames = badLayerHandler->badLayerNames();
139 const QStringList restrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *prj );
140 for ( const QString &badLayerId : badLayerIds )
141 {
142 // if this bad layer is in restricted layers
143 // it doesn't need to be added to unrestricted bad layers
144 if ( badLayerNames.contains( badLayerId ) && restrictedLayers.contains( badLayerNames.value( badLayerId ) ) )
145 {
146 continue;
147 }
148 unrestrictedBadLayers.append( badLayerId );
149 }
150 if ( !unrestrictedBadLayers.isEmpty() )
151 {
152 // This is a critical error unless QGIS_SERVER_IGNORE_BAD_LAYERS is set to TRUE
153 if ( !settings || !settings->ignoreBadLayers() )
154 {
155 QgsMessageLog::logMessage( u"Error, Layer(s) %1 not valid in project %2"_s.arg( unrestrictedBadLayers.join( ", "_L1 ), path ), u"Server"_s, Qgis::MessageLevel::Critical );
156 throw QgsServerException( u"Layer(s) not valid"_s );
157 }
158 else
159 {
160 QgsMessageLog::logMessage( u"Warning, Layer(s) %1 not valid in project %2"_s.arg( unrestrictedBadLayers.join( ", "_L1 ), path ), u"Server"_s, Qgis::MessageLevel::Warning );
161 }
162 }
163 }
164 cacheProject( path, prj.release() );
165 }
166 else
167 {
168 QgsMessageLog::logMessage( u"Error when loading project file '%1': %2 "_s.arg( path, prj->error() ), u"Server"_s, Qgis::MessageLevel::Critical );
169 }
170 }
171
172 auto entry = mProjectCache[path];
173 if ( !entry )
174 {
175 return nullptr;
176 }
177
178 //Try to reload data sources of invalid layers
179 if ( ( settings && settings->retryBadLayers() ) && ( entry->second->validCount() != entry->second->count() ) )
180 {
181 for ( const auto &l : entry->second->mapLayers() )
182 {
183 if ( !l->isValid() )
184 {
185 QString subsetString;
186 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( l );
187 if ( vlayer )
188 {
189 subsetString = vlayer->subsetString();
190 }
192 l->setDataSource( l->source(), l->name(), l->providerType(), options );
193 if ( vlayer && !subsetString.isEmpty() )
194 {
195 vlayer->setSubsetString( subsetString );
196 }
197 }
198 }
199 }
200
201 return entry->second.get();
202}
203
204QList<QgsProject *> QgsConfigCache::projects() const
205{
206 QList<QgsProject *> projects;
207
208 const auto constKeys { mProjectCache.keys() };
209 for ( const auto &path : std::as_const( constKeys ) )
210 {
211 projects << mProjectCache[path]->second.get();
212 }
213
214 return projects;
215}
216
217QDomDocument *QgsConfigCache::xmlDocument( const QString &filePath )
218{
219 //first open file
220 QFile configFile( filePath );
221 if ( !configFile.exists() )
222 {
223 QgsMessageLog::logMessage( "Error, configuration file '" + filePath + "' does not exist", u"Server"_s, Qgis::MessageLevel::Critical );
224 return nullptr;
225 }
226
227 if ( !configFile.open( QIODevice::ReadOnly ) )
228 {
229 QgsMessageLog::logMessage( "Error, cannot open configuration file '" + filePath + "'", u"Server"_s, Qgis::MessageLevel::Critical );
230 return nullptr;
231 }
232
233 // first get cache
234 QDomDocument *xmlDoc = mXmlDocumentCache.object( filePath );
235 if ( !xmlDoc )
236 {
237 //then create xml document
238 xmlDoc = new QDomDocument();
239 QString errorMsg;
240 int line, column;
241 if ( !xmlDoc->setContent( &configFile, true, &errorMsg, &line, &column ) )
242 {
243 QgsMessageLog::logMessage( "Error parsing file '" + filePath + u"': parse error %1 at row %2, column %3"_s.arg( errorMsg ).arg( line ).arg( column ), u"Server"_s, Qgis::MessageLevel::Critical );
244 delete xmlDoc;
245 return nullptr;
246 }
247 mXmlDocumentCache.insert( filePath, xmlDoc );
248 xmlDoc = mXmlDocumentCache.object( filePath );
249 Q_ASSERT( xmlDoc );
250 }
251 return xmlDoc;
252}
253
254
255void QgsConfigCache::cacheProject( const QString &path, QgsProject *project )
256{
257 mProjectCache.insert( path, new std::pair<QDateTime, std::unique_ptr<QgsProject>>( project->lastModified(), std::unique_ptr<QgsProject>( project ) ) );
258
259 mStrategy->entryInserted( path );
260}
261
262void QgsConfigCache::removeEntry( const QString &path )
263{
264 mProjectCache.remove( path );
265
266 //xml document must be removed last, as other config cache destructors may require it
267 mXmlDocumentCache.remove( path );
268
269 mStrategy->entryRemoved( path );
270
271 emit projectRemovedFromCache( path );
272}
273
274// slots
275
276void QgsConfigCache::removeChangedEntry( const QString &path )
277{
278 removeEntry( path );
279}
280
281
283{
284 // QCache::keys returns a QList so it is safe
285 // to mutate while iterating
286 const auto constKeys { mProjectCache.keys() };
287 for ( const auto &path : std::as_const( constKeys ) )
288 {
289 const auto entry = mProjectCache[path];
290 if ( entry && entry->first < entry->second->lastModified() )
291 {
292 removeEntry( path );
293 }
294 }
295}
296
297// File system invalidation strategy
298
299
302
304{
305 QObject::connect( &mFileSystemWatcher, &QFileSystemWatcher::fileChanged, cache, &QgsConfigCache::removeChangedEntry );
306}
307
309{
310 mFileSystemWatcher.removePath( path );
311}
312
314{
315 mFileSystemWatcher.addPath( path );
316}
317
318// Periodic invalidation strategy
319
321 : mInterval( interval )
322{}
323
325{
326 QObject::connect( &mTimer, &QTimer::timeout, cache, &QgsConfigCache::removeChangedEntries );
327}
328
329
330void QgsPeriodicCacheStrategy::entryRemoved( const QString &path )
331{
332 Q_UNUSED( path )
333 // No-op
334}
335
337{
338 Q_UNUSED( path )
339 if ( !mTimer.isActive() )
340 {
341 mTimer.start( mInterval );
342 }
343}
344
346{
347 if ( mTimer.isActive() )
348 {
349 // Restart timer
350 mTimer.start( msec );
351 }
352}
353
354
355// Null strategy
356
358{
359 Q_UNUSED( cache )
360}
361
362void QgsNullCacheStrategy::entryRemoved( const QString &path )
363{
364 Q_UNUSED( path )
365}
366
367void QgsNullCacheStrategy::entryInserted( const QString &path )
368{
369 Q_UNUSED( path )
370}
@ DontLoad3DViews
Skip loading 3D views.
Definition qgis.h:4451
@ DontStoreOriginalStyles
Skip the initial XML style storage for layers. Useful for minimising project load times in non-intera...
Definition qgis.h:4450
@ ForceReadOnlyLayers
Open layers in a read-only mode.
Definition qgis.h:4453
@ TrustLayerMetadata
Trust layer metadata. Improves project read time. Do not use it if layers' extent is not fixed during...
Definition qgis.h:4448
@ DontUpgradeAnnotations
Don't upgrade old annotation items to QgsAnnotationItem.
Definition qgis.h:4454
@ DontLoadLayouts
Don't load print layouts. Improves project read time if layouts are not required, and allows projects...
Definition qgis.h:4446
QFlags< ProjectCapability > ProjectCapabilities
Flags which control project capabilities.
Definition qgis.h:4487
QFlags< ProjectReadFlag > ProjectReadFlags
Project load flags.
Definition qgis.h:4465
@ Warning
Warning message.
Definition qgis.h:162
@ Critical
Critical/error message.
Definition qgis.h:163
@ Info
Information message.
Definition qgis.h:161
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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:113
static void setInstance(QgsProject *project)
Set the current project singleton instance to project.
Exception base class for server exceptions.
static QStringList wmsRestrictedLayers(const QgsProject &project)
Returns the restricted layer name list.
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 projectCacheSize() const
Returns the projects cache size The default value is 100, the value can be changed by setting the env...
int projectCacheCheckInterval() const
Returns the config cache check interval (in ms) for the 'periodic' strategy.
bool retryBadLayers() const
Returns true if bad layers should be re-checked after the project has been cached.
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 for bad (broken) layers.
QMap< QString, QString > badLayerNames() const
Returns names of bad layers with ids.
QStringList badLayers() const
badLayers
Represents a vector layer which manages a vector based dataset.
virtual bool setSubsetString(const QString &subset)
Sets the string (typically sql) used to define a subset of the layer.
QgsAbstractCacheStrategy * getStrategyFromSettings(QgsServerSettings *settings)
Setting options for creating vector data providers.