Skip to content

Commit

Permalink
Fix layer cache to work with layers which have the same id (but in di…
Browse files Browse the repository at this point in the history
…fferent project files)
  • Loading branch information
mhugent committed Jul 3, 2015
1 parent 339dbf1 commit 127dd64
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 34 deletions.
54 changes: 37 additions & 17 deletions src/server/qgsmslayercache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
***************************************************************************/

#include "qgsmslayercache.h"
#include "qgsmessagelog.h"
#include "qgsvectorlayer.h"
#include "qgslogger.h"
#include <QFile>
Expand Down Expand Up @@ -55,18 +56,13 @@ QgsMSLayerCache::~QgsMSLayerCache()

void QgsMSLayerCache::insertLayer( const QString& url, const QString& layerName, QgsMapLayer* layer, const QString& configFile, const QList<QString>& tempFiles )
{
QgsDebugMsg( "inserting layer" );
QgsMessageLog::logMessage( "Layer cache: insert Layer '" + layerName + "' configFile: " + configFile, "Server", QgsMessageLog::INFO );
if ( mEntries.size() > std::max( mDefaultMaxLayers, mProjectMaxLayers ) ) //force cache layer examination after 10 inserted layers
{
updateEntries();
}

QPair<QString, QString> urlLayerPair = qMakePair( url, layerName );
QHash<QPair<QString, QString>, QgsMSLayerCacheEntry>::iterator it = mEntries.find( urlLayerPair );
if ( it != mEntries.end() )
{
delete it.value().layerPointer;
}

QgsMSLayerCacheEntry newEntry;
newEntry.layerPointer = layer;
Expand Down Expand Up @@ -94,41 +90,55 @@ void QgsMSLayerCache::insertLayer( const QString& url, const QString& layerName,
}
}

QgsMapLayer* QgsMSLayerCache::searchLayer( const QString& url, const QString& layerName )
QgsMapLayer* QgsMSLayerCache::searchLayer( const QString& url, const QString& layerName, const QString& configFile )
{
QPair<QString, QString> urlNamePair = qMakePair( url, layerName );
if ( !mEntries.contains( urlNamePair ) )
{
QgsDebugMsg( "Layer not found in cache" );
QgsMessageLog::logMessage( "Layer '" + layerName + "' configFile: " + configFile + " not found in layer cache'", "Server", QgsMessageLog::INFO );
return 0;
}
else
{
QgsMSLayerCacheEntry &entry = mEntries[ urlNamePair ];
entry.lastUsedTime = time( NULL );
QgsDebugMsg( "Layer found in cache" );
return entry.layerPointer;
QList< QgsMSLayerCacheEntry > layers = mEntries.values( urlNamePair );
QList< QgsMSLayerCacheEntry >::iterator layerIt = layers.begin();
for ( ; layerIt != layers.end(); ++layerIt )
{
if ( configFile.isEmpty() || layerIt->configFile == configFile )
{
layerIt->lastUsedTime = time( NULL );
QgsMessageLog::logMessage( "Layer '" + layerName + "' configFile: " + configFile + " found in layer cache", "Server", QgsMessageLog::INFO );
return layerIt->layerPointer;
}
}
QgsMessageLog::logMessage( "Layer '" + layerName + "' configFile: " + configFile + " not found in layer cache'", "Server", QgsMessageLog::INFO );
return 0;
}
}

void QgsMSLayerCache::removeProjectFileLayers( const QString& project )
{
QgsMessageLog::logMessage( "Removing cache entries for project file: " + project, "Server", QgsMessageLog::INFO );
QList< QPair< QString, QString > > removeEntries;
QList< QgsMSLayerCacheEntry > removeEntriesValues;

QHash<QPair<QString, QString>, QgsMSLayerCacheEntry>::iterator entryIt = mEntries.begin();
for ( ; entryIt != mEntries.end(); ++entryIt )
{
if ( entryIt.value().configFile == project )
{
removeEntries.push_back( entryIt.key() );
removeEntriesValues.push_back( entryIt.value() );
freeEntryRessources( entryIt.value() );
}
}

QList< QPair< QString, QString > >::const_iterator removeIt = removeEntries.constBegin();
for ( ; removeIt != removeEntries.constEnd(); ++removeIt )
for ( int i = 0; i < removeEntries.size(); ++i )
{
mEntries.remove( *removeIt );
const QgsMSLayerCacheEntry& removeEntry = removeEntriesValues.at( i );
const QPair< QString, QString > removeKey = removeEntries.at( i );
QgsMessageLog::logMessage( "Removing cache entry for url:" + removeKey.first + " layerName:" + removeKey.second + " project file:" + project, "Server", QgsMessageLog::INFO );
mEntries.remove( removeKey, removeEntry );
}
}

Expand All @@ -149,12 +159,11 @@ void QgsMSLayerCache::updateEntries()

void QgsMSLayerCache::removeLeastUsedEntry()
{

if ( mEntries.size() < 1 )
{
return;
}
QgsDebugMsg( "removeLeastUsedEntry" );

QHash<QPair<QString, QString>, QgsMSLayerCacheEntry>::iterator it = mEntries.begin();
QHash<QPair<QString, QString>, QgsMSLayerCacheEntry>::iterator lowest_it = it;
time_t lowest_time = it->lastUsedTime;
Expand All @@ -168,6 +177,7 @@ void QgsMSLayerCache::removeLeastUsedEntry()
}
}

QgsMessageLog::logMessage( "Removing last accessed layer '" + lowest_it.value().layerPointer->name() + "' project file " + lowest_it.value().configFile + " from cache" , "Server", QgsMessageLog::INFO );
freeEntryRessources( *lowest_it );
mEntries.erase( lowest_it );
}
Expand Down Expand Up @@ -203,3 +213,13 @@ void QgsMSLayerCache::freeEntryRessources( QgsMSLayerCacheEntry& entry )
}
}
}

void QgsMSLayerCache::logCacheContents() const
{
QgsMessageLog::logMessage( "Layer cache contents:" , "Server", QgsMessageLog::INFO );
QHash<QPair<QString, QString>, QgsMSLayerCacheEntry>::const_iterator it = mEntries.constBegin();
for ( ; it != mEntries.constEnd(); ++it )
{
QgsMessageLog::logMessage( "Url: " + it.value().url + " Layer name: " + it.value().layerPointer->name() + " Project: " + it.value().configFile, "Server", QgsMessageLog::INFO );
}
}
45 changes: 29 additions & 16 deletions src/server/qgsmslayercache.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

#include <time.h>
#include <QFileSystemWatcher>
#include <QHash>
#include <QMultiHash>
#include <QObject>
#include <QPair>
#include <QString>
Expand All @@ -35,9 +35,19 @@ struct QgsMSLayerCacheEntry
QgsMapLayer* layerPointer;
QList<QString> temporaryFiles; //path to the temporary files written for the layer
QString configFile; //path to the project file associated with the layer

bool operator==( const QgsMSLayerCacheEntry& other ) const
{
return ( creationTime == other.creationTime
&& lastUsedTime == other.lastUsedTime
&& url == other.url
&& layerPointer == other.layerPointer
&& temporaryFiles == other.temporaryFiles
&& configFile == other.configFile );
}
};

/**A singleton class that caches layer objects for the
/** A singleton class that caches layer objects for the
QGIS mapserver*/
class QgsMSLayerCache: public QObject
{
Expand All @@ -46,53 +56,56 @@ class QgsMSLayerCache: public QObject
static QgsMSLayerCache* instance();
~QgsMSLayerCache();

/**Inserts a new layer into the cash
/** Inserts a new layer into the cash
@param url the layer datasource
@param layerName the layer name (to distinguish between different layers in a request using the same datasource
@param configFile path of the config file (to invalidate entries if file changes). Can be empty (e.g. layers from sld)
@param tempFiles some layers have temporary files. The cash makes sure they are removed when removing the layer from the cash*/
void insertLayer( const QString& url, const QString& layerName, QgsMapLayer* layer, const QString& configFile = QString(), const QList<QString>& tempFiles = QList<QString>() );
/**Searches for the layer with the given url.
/** Searches for the layer with the given url.
@return a pointer to the layer or 0 if no such layer*/
QgsMapLayer* searchLayer( const QString& url, const QString& layerName );
QgsMapLayer* searchLayer( const QString& url, const QString& layerName, const QString& configFile = QString() );

int projectsMaxLayers() const { return mProjectMaxLayers; }

void setProjectMaxLayers( int n ) { mProjectMaxLayers = n; }

//for debugging
void logCacheContents() const;

protected:
/**Protected singleton constructor*/
/** Protected singleton constructor*/
QgsMSLayerCache();
/**Goes through the list and removes entries and layers
/** Goes through the list and removes entries and layers
depending on their time stamps and the number of other
layers*/
void updateEntries();
/**Removes the cash entry with the lowest 'lastUsedTime'*/
/** Removes the cash entry with the lowest 'lastUsedTime'*/
void removeLeastUsedEntry();
/**Frees memory and removes temporary files of an entry*/
/** Frees memory and removes temporary files of an entry*/
void freeEntryRessources( QgsMSLayerCacheEntry& entry );

private:
/**Cash entries with pair url/layer name as a key. The layer name is necessary for cases where the same
/** Cash entries with pair url/layer name as a key. The layer name is necessary for cases where the same
url is used several time in a request. It ensures that different layer instances are created for different
layer names*/
QHash<QPair<QString, QString>, QgsMSLayerCacheEntry> mEntries;
QMultiHash<QPair<QString, QString>, QgsMSLayerCacheEntry> mEntries;

/**Config files used in the cache (with reference counter)*/
/** Config files used in the cache (with reference counter)*/
QHash< QString, int > mConfigFiles;

/**Check for configuration file updates (remove layers from cache if configuration file changes)*/
/** Check for configuration file updates (remove layers from cache if configuration file changes)*/
QFileSystemWatcher mFileSystemWatcher;

/**Maximum number of layers in the cache*/
/** Maximum number of layers in the cache*/
int mDefaultMaxLayers;

/**Maximum number of layers in the cache, overrides DEFAULT_MAX_N_LAYERS if larger*/
/** Maximum number of layers in the cache, overrides DEFAULT_MAX_N_LAYERS if larger*/
int mProjectMaxLayers;

private slots:

/**Removes entries from a project (e.g. if a project file has changed)*/
/** Removes entries from a project (e.g. if a project file has changed)*/
void removeProjectFileLayers( const QString& project );
};

Expand Down
2 changes: 2 additions & 0 deletions src/server/qgsserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@ bool QgsServer::init( int & argc, char ** argv )
}
#endif

QgsServerLogger::instance();

QgsEditorWidgetRegistry::initEditors();
mInitialised = TRUE;
return TRUE;
Expand Down
2 changes: 1 addition & 1 deletion src/server/qgsserverprojectparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ QgsMapLayer* QgsServerProjectParser::createLayerFromElement( const QDomElement&
QgsMapLayer* layer = 0;
if ( useCache )
{
layer = QgsMSLayerCache::instance()->searchLayer( absoluteUri, id );
layer = QgsMSLayerCache::instance()->searchLayer( absoluteUri, id, mProjectPath );
}

if ( layer )
Expand Down

0 comments on commit 127dd64

Please sign in to comment.