QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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"
20 #include "qgsstorebadlayerinfo.h"
21 #include "qgsserverprojectutils.h"
22 
23 #include <QFile>
24 
25 QgsConfigCache *QgsConfigCache::sInstance = nullptr;
26 
27 
29 {
30  QgsAbstractCacheStrategy *strategy;
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 
92 QgsConfigCache::QgsConfigCache() : QgsConfigCache( new QgsFileSystemCacheStrategy() )
93 {
94 
95 }
96 
97 const 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 don't load layouts flag
121  if ( settings->getPrintDisabled() )
122  {
123  readFlags |= Qgis::ProjectReadFlag::DontLoadLayouts;
124  }
125  }
126 
127  if ( prj->read( path, readFlags ) )
128  {
129  if ( !badLayerHandler->badLayers().isEmpty() )
130  {
131  // if bad layers are not restricted layers so service failed
132  QStringList unrestrictedBadLayers;
133  // test bad layers through restrictedlayers
134  const QStringList badLayerIds = badLayerHandler->badLayers();
135  const QMap<QString, QString> badLayerNames = badLayerHandler->badLayerNames();
136  const QStringList resctrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *prj );
137  for ( const QString &badLayerId : badLayerIds )
138  {
139  // if this bad layer is in restricted layers
140  // it doesn't need to be added to unrestricted bad layers
141  if ( badLayerNames.contains( badLayerId ) &&
142  resctrictedLayers.contains( badLayerNames.value( badLayerId ) ) )
143  {
144  continue;
145  }
146  unrestrictedBadLayers.append( badLayerId );
147  }
148  if ( !unrestrictedBadLayers.isEmpty() )
149  {
150  // This is a critical error unless QGIS_SERVER_IGNORE_BAD_LAYERS is set to TRUE
151  if ( ! settings || ! settings->ignoreBadLayers() )
152  {
154  QStringLiteral( "Error, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QLatin1String( ", " ) ), path ),
155  QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
156  throw QgsServerException( QStringLiteral( "Layer(s) not valid" ) );
157  }
158  else
159  {
161  QStringLiteral( "Warning, Layer(s) %1 not valid in project %2" ).arg( unrestrictedBadLayers.join( QLatin1String( ", " ) ), path ),
162  QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
163  }
164  }
165  }
166  cacheProject( path, prj.release() );
167  }
168  else
169  {
171  QStringLiteral( "Error when loading project file '%1': %2 " ).arg( path, prj->error() ),
172  QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
173  }
174  }
175 
176  auto entry = mProjectCache[ path ];
177  return entry ? entry->second.get() : nullptr;
178 }
179 
180 QDomDocument *QgsConfigCache::xmlDocument( const QString &filePath )
181 {
182  //first open file
183  QFile configFile( filePath );
184  if ( !configFile.exists() )
185  {
186  QgsMessageLog::logMessage( "Error, configuration file '" + filePath + "' does not exist", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
187  return nullptr;
188  }
189 
190  if ( !configFile.open( QIODevice::ReadOnly ) )
191  {
192  QgsMessageLog::logMessage( "Error, cannot open configuration file '" + filePath + "'", QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
193  return nullptr;
194  }
195 
196  // first get cache
197  QDomDocument *xmlDoc = mXmlDocumentCache.object( filePath );
198  if ( !xmlDoc )
199  {
200  //then create xml document
201  xmlDoc = new QDomDocument();
202  QString errorMsg;
203  int line, column;
204  if ( !xmlDoc->setContent( &configFile, true, &errorMsg, &line, &column ) )
205  {
206  QgsMessageLog::logMessage( "Error parsing file '" + filePath +
207  QStringLiteral( "': parse error %1 at row %2, column %3" ).arg( errorMsg ).arg( line ).arg( column ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
208  delete xmlDoc;
209  return nullptr;
210  }
211  mXmlDocumentCache.insert( filePath, xmlDoc );
212  xmlDoc = mXmlDocumentCache.object( filePath );
213  Q_ASSERT( xmlDoc );
214  }
215  return xmlDoc;
216 }
217 
218 
219 void QgsConfigCache::cacheProject( const QString &path, QgsProject *project )
220 {
221  mProjectCache.insert( path, new std::pair<QDateTime, std::unique_ptr<QgsProject> >( project->lastModified(), std::unique_ptr<QgsProject>( project ) ) );
222 
223  mStrategy->entryInserted( path );
224 }
225 
226 void QgsConfigCache::removeEntry( const QString &path )
227 {
228  mProjectCache.remove( path );
229 
230  //xml document must be removed last, as other config cache destructors may require it
231  mXmlDocumentCache.remove( path );
232 
233  mStrategy->entryRemoved( path );
234 }
235 
236 // slots
237 
238 void QgsConfigCache::removeChangedEntry( const QString &path )
239 {
240  removeEntry( path );
241 }
242 
243 
245 {
246  // QCache::keys returns a QList so it is safe
247  // to mutate while iterating
248  const auto constKeys { mProjectCache.keys() };
249  for ( const auto &path : std::as_const( constKeys ) )
250  {
251  const auto entry = mProjectCache[ path ];
252  if ( entry && entry->first < entry->second->lastModified() )
253  {
254  removeEntry( path );
255  }
256  }
257 }
258 
259 // File system invalidation strategy
260 
261 
262 
264 {
265 }
266 
268 {
269  QObject::connect( &mFileSystemWatcher, &QFileSystemWatcher::fileChanged, cache, &QgsConfigCache::removeChangedEntry );
270 }
271 
272 void QgsFileSystemCacheStrategy::entryRemoved( const QString &path )
273 {
274  mFileSystemWatcher.removePath( path );
275 }
276 
277 void QgsFileSystemCacheStrategy::entryInserted( const QString &path )
278 {
279  mFileSystemWatcher.addPath( path );
280 }
281 
282 // Periodic invalidation strategy
283 
285  : mInterval( interval )
286 {
287 }
288 
290 {
291  QObject::connect( &mTimer, &QTimer::timeout, cache, &QgsConfigCache::removeChangedEntries );
292 }
293 
294 
295 
296 void QgsPeriodicCacheStrategy::entryRemoved( const QString &path )
297 {
298  Q_UNUSED( path )
299  // No-op
300 }
301 
302 void QgsPeriodicCacheStrategy::entryInserted( const QString &path )
303 {
304  Q_UNUSED( path )
305  if ( !mTimer.isActive() )
306  {
307  mTimer.start( mInterval );
308  }
309 }
310 
312 {
313  if ( mTimer.isActive() )
314  {
315  // Restart timer
316  mTimer.start( msec );
317  }
318 }
319 
320 
321 // Null strategy
322 
324 {
325  Q_UNUSED( cache )
326 }
327 
328 void QgsNullCacheStrategy::entryRemoved( const QString &path )
329 {
330  Q_UNUSED( path )
331 }
332 
333 void QgsNullCacheStrategy::entryInserted( const QString &path )
334 {
335  Q_UNUSED( path )
336 }
337 
QgsStoreBadLayerInfo::badLayerNames
QMap< QString, QString > badLayerNames() const
Returns names of bad layers with ids.
Definition: qgsstorebadlayerinfo.h:69
QgsServerSettings::getPrintDisabled
bool getPrintDisabled() const
Returns true if WMS GetPrint request is disabled and the project's reading flag QgsProject::ReadFlag:...
Definition: qgsserversettings.cpp:604
QgsConfigCache::removeEntry
void removeEntry(const QString &path)
Removes an entry from cache.
Definition: qgsconfigcache.cpp:226
QgsPeriodicCacheStrategy::attach
void attach(QgsConfigCache *owner) override
Attaches cache to this strategy.
Definition: qgsconfigcache.cpp:289
QgsNullCacheStrategy::attach
void attach(QgsConfigCache *owner) override
Attaches cache to this strategy.
Definition: qgsconfigcache.cpp:323
QgsConfigCache
Cache for server configuration.
Definition: qgsconfigcache.h:77
QgsNullCacheStrategy::entryRemoved
void entryRemoved(const QString &path) override
Called when an entry is removed from cache.
Definition: qgsconfigcache.cpp:328
qgsconfigcache.h
QgsPeriodicCacheStrategy
Periodic system cache strategy for server configuration.
Definition: qgsconfigcache.h:197
QgsConfigCache::instance
static QgsConfigCache * instance()
Returns the current instance.
Definition: qgsconfigcache.cpp:70
QgsNullCacheStrategy::entryInserted
void entryInserted(const QString &path) override
Called when an entry is inserted.
Definition: qgsconfigcache.cpp:333
qgsserverprojectutils.h
getStrategyFromSettings
QgsAbstractCacheStrategy * getStrategyFromSettings(QgsServerSettings *settings)
Definition: qgsconfigcache.cpp:28
QgsPeriodicCacheStrategy::setCheckInterval
void setCheckInterval(int msec)
Sets the invalidation check interval for PeriodicStrategy.
Definition: qgsconfigcache.cpp:311
QgsPeriodicCacheStrategy::entryInserted
void entryInserted(const QString &path) override
Called when an entry is inserted.
Definition: qgsconfigcache.cpp:302
QgsServerSettings::projectCacheStrategy
QString projectCacheStrategy() const
Returns the project's cache strategy The default value is 'filesystem', the value can be changed by s...
Definition: qgsserversettings.cpp:647
qgsstorebadlayerinfo.h
QgsStoreBadLayerInfo::badLayers
QStringList badLayers() const
badLayers
Definition: qgsstorebadlayerinfo.h:63
QgsProject::lastModified
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
Definition: qgsproject.cpp:798
QgsProject
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:103
QgsServerSettings::trustLayerMetadata
bool trustLayerMetadata() const
Returns true if the reading flag trust layer metadata is activated.
Definition: qgsserversettings.cpp:599
QgsServerSettings
Provides a way to retrieve settings by prioritizing according to environment variables,...
Definition: qgsserversettings.h:92
QgsFileSystemCacheStrategy::entryRemoved
void entryRemoved(const QString &path) override
Called when an entry is removed from cache.
Definition: qgsconfigcache.cpp:272
QgsConfigCache::removeChangedEntry
void removeChangedEntry(const QString &path)
Remove cache entry.
Definition: qgsconfigcache.cpp:238
QgsNullCacheStrategy
Null system cache strategy for server configuration, completely disable cache invalidation invalidati...
Definition: qgsconfigcache.h:251
QgsPeriodicCacheStrategy::QgsPeriodicCacheStrategy
QgsPeriodicCacheStrategy(int interval=3000)
Creates a new periodic strategy.
Definition: qgsconfigcache.cpp:284
QgsProject::setInstance
static void setInstance(QgsProject *project)
Set the current project singleton instance to project.
Definition: qgsproject.cpp:474
QgsServerProjectUtils::wmsRestrictedLayers
SERVER_EXPORT QStringList wmsRestrictedLayers(const QgsProject &project)
Returns the restricted layer name list.
Definition: qgsserverprojectutils.cpp:441
QgsFileSystemCacheStrategy::entryInserted
void entryInserted(const QString &path) override
Called when an entry is inserted.
Definition: qgsconfigcache.cpp:277
QgsMessageLog::logMessage
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).
Definition: qgsmessagelog.cpp:27
QgsConfigCache::QgsConfigCache
QgsConfigCache(QgsServerSettings *settings)
Initialize from settings.
Definition: qgsconfigcache.cpp:80
QgsFileSystemCacheStrategy::QgsFileSystemCacheStrategy
QgsFileSystemCacheStrategy()
Creates a new filesystem strategy.
Definition: qgsconfigcache.cpp:263
QgsServerSettings::ignoreBadLayers
bool ignoreBadLayers() const
Returns true if the bad layers are ignored and false when the presence of a bad layers invalidates th...
Definition: qgsserversettings.cpp:594
QgsFileSystemCacheStrategy
File system cache strategy for server configuration.
Definition: qgsconfigcache.h:162
QgsServerException
Exception base class for server exceptions.
Definition: qgsserverexception.h:42
QgsFileSystemCacheStrategy::attach
void attach(QgsConfigCache *cache) override
Attach cache to this strategy.
Definition: qgsconfigcache.cpp:267
QgsConfigCache::removeChangedEntries
void removeChangedEntries()
Remove all changed cache entries.
Definition: qgsconfigcache.cpp:244
QgsStoreBadLayerInfo
Stores layer ids of bad layers.
Definition: qgsstorebadlayerinfo.h:31
qgsserverexception.h
QgsPeriodicCacheStrategy::entryRemoved
void entryRemoved(const QString &path) override
Called when an entry is removed from cache.
Definition: qgsconfigcache.cpp:296
QgsConfigCache::project
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.
Definition: qgsconfigcache.cpp:97
qgsmessagelog.h
QgsServerSettings::projectCacheCheckInterval
int projectCacheCheckInterval() const
Returns the config cache check interval for the 'periodic' strategy.
Definition: qgsserversettings.cpp:642
QgsAbstractCacheStrategy
Abstract base class for implementing cache invalidation strategy.
Definition: qgsconfigcache.h:45
QgsConfigCache::initialize
static void initialize(QgsServerSettings *settings)
Initialize from settings.
Definition: qgsconfigcache.cpp:57