Skip to content
Permalink
Browse files

Merge pull request #2191 from mhugent/server_cache_multi_projects

Fix layer cache to work with layers which have the same id (but in di…
  • Loading branch information
mhugent committed Jul 3, 2015
2 parents f92d9c1 + 127dd64 commit 52cfb56e7e240242a6f667e707915675753dd732
Showing with 69 additions and 34 deletions.
  1. +37 −17 src/server/qgsmslayercache.cpp
  2. +29 −16 src/server/qgsmslayercache.h
  3. +2 −0 src/server/qgsserver.cpp
  4. +1 −1 src/server/qgsserverprojectparser.cpp
@@ -16,6 +16,7 @@
***************************************************************************/

#include "qgsmslayercache.h"
#include "qgsmessagelog.h"
#include "qgsvectorlayer.h"
#include "qgslogger.h"
#include <QFile>
@@ -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;
@@ -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 );
}
}

@@ -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;
@@ -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 );
}
@@ -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 );
}
}
@@ -20,7 +20,7 @@

#include <time.h>
#include <QFileSystemWatcher>
#include <QHash>
#include <QMultiHash>
#include <QObject>
#include <QPair>
#include <QString>
@@ -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
{
@@ -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 );
};

@@ -394,6 +394,8 @@ bool QgsServer::init( int & argc, char ** argv )
}
#endif

QgsServerLogger::instance();

QgsEditorWidgetRegistry::initEditors();
mInitialised = TRUE;
QgsMessageLog::logMessage( "Server intialised", "Server", QgsMessageLog::INFO );
@@ -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 )

0 comments on commit 52cfb56

Please sign in to comment.
You can’t perform that action at this time.