Server refactoring: implements service modules registry

Implementation for https://github.com/qgis/QGIS-Enhancement-Proposals/issues/74
This commit is contained in:
David Marteau 2016-12-07 22:09:57 +01:00
parent 3a03c98a08
commit 1e0d830529
26 changed files with 1746 additions and 0 deletions

View File

@ -0,0 +1 @@
---

View File

@ -0,0 +1,76 @@
/***************************************************************************
qgsserverrequest.h
Define ruquest class for getting request contents
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/**
* \ingroup server
* QgsServerRequest
* Class defining request intreface passed to services QgsService::executeRequest() method
*
* Note about design: this intreface must be passed along to python and thus signatures methods must be
* compatible with pyQGS/pyQT api and rules.
*/
class QgsServerRequest
{
%TypeHeaderCode
#include "qgsserverrequest.h"
%End
public:
enum Method {
HeadMethod, PutMethod, GetMethod, PostMethod, DeleteMethod
};
/**
* Constructor
*
* @param url the lurl string
* @param method the request method
*/
QgsServerRequest( const QString& url, Method method );
/**
* Constructor
*
* @param url QUrl
* @param method the rquest method
*/
QgsServerRequest( const QUrl& url, Method method );
//! destructor
virtual ~QgsServerRequest();
/**
* @return the request url
*/
virtual const QUrl& url() const;
/**
* @return the rquest method
*/
virtual Method method() const;
/**
* Return post/put data
* The default implementation retfurn nullptr
* @return a QByteArray pointer or nullptr
*/
virtual const QByteArray* data() const;
};

View File

@ -0,0 +1,77 @@
/***************************************************************************
qgsserverresponse.h
Define response class for services
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/**
* \ingroup server
* QgsServerResponse
* Class defining response interface passed to services QgsService::executeRequest() method
*
* Note:
* This class is intended to be used from python code: method signatures and return types should be
* compatible with pyQGIS/pyQT types and rules.
*
*/
class QgsServerResponse
{
%TypeHeaderCode
#include "qgsserverresponse.h"
%End
public:
//!constructor
QgsServerResponse();
//! destructor
virtual ~QgsServerResponse();
/** Set header entry
* Add header entry to the response
* Note that it is usually an error to set hedaer after writng data
*/
virtual void setHeader( const QString& key, const QString& value ) = 0;
/** Set the http return code
*/
virtual void setReturnCode( int code ) = 0;
/**
* Send error
*/
virtual void sendError( int code, const QString& message ) = 0;
/**
* Write string
*/
virtual void write(const QString& data );
/**
* Write chunk af data
* They are convenience method that will write directly to the
* underlying I/O device
*/
virtual qint64 write(const QByteArray& byteArray );
/**
* Return the underlying QIODevice
*/
virtual QIODevice* io() = 0;
};

View File

@ -0,0 +1,60 @@
/***************************************************************************
qgsservice.h
Class defining the service interface for QGIS server services.
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/**
* \ingroup server
* QgsService
* Class defining interfaces for QGIS server services
*
* This class provides methods for executing server requests
* They are registered at runtime for a given service name.
*
*/
class QgsService
{
%TypeHeaderCode
#include "qgsservice.h"
#include "qgsserverrequest.h"
#include "qgsserverresponse.h"
%End
public:
//! Constructor
QgsService();
//! Destructor
virtual ~QgsService();
/**
* Return true if the given method is supported for that
* service.
* @param method QGSMethodType the
* @return QString containing the configuration file path
*/
virtual bool allowMethod( QgsServerRequest::Method ) const = 0;
/**
* Execute the requests and set result in QgsServerRequest
* @param request a QgsServerRequest instance
* @param response a QgsServerResponse instance
*/
virtual void executeRequest( const QgsServerRequest& request, QgsServerResponse& response ) = 0;
};

View File

@ -0,0 +1,50 @@
/***************************************************************************
qgsservicemodule.h
Class defining the service module interface for QGIS server services.
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/**
* \ingroup server
* QgsServiceModule
* Class defining the service module interface for QGIS server services
*
* This class act as a service registrar for services.
*
* For dynamic modules, a QgsServiceModule instance is returned from the QGS_ServiceModule_Init() entry point
*/
class QgsServiceModule
{
%TypeHeaderCode
#include "qgsservicemodule.h"
#include "qgsserviceregistry.h"
%End
public:
//! Constructor
QgsServiceModule();
//! Destructor
virtual ~QgsServiceModule() = 0;
/**
* Ask module to registe all provided services
* @param registry QgsServiceRegistry
*/
virtual void registerSelf( QgsServiceRegistry& registry ) = 0;
};

View File

@ -0,0 +1,83 @@
/***************************************************************************
qgsserviceregistry.h
Class defining the service manager for QGIS server services.
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/**
* \ingroup server
* QgsServiceRegistry
* Class defining the cegistry manager for QGIS server services
*
* This class provides methods for registering and retrieving
* services. Note that the regstiry does not hord ownership of
* registered service but vill call the 'release' method on cleanup)
*
*/
class QgsServiceRegistry
{
%TypeHeaderCode
#include "qgsserviceregistry.h"
#include "qgsservice.h"
#include "qgsserverrequest.h"
#include "qgsserverresponse.h"
%End
public:
//! Constructor
QgsServiceRegistry();
//! Destructor
~QgsServiceRegistry();
/**
* Retrieve a service from its name
* @param name the name of the service
* @param version the required version (optional)
* @return QgsService
*/
QgsService* getService( const QString& name, const QString& version = QString() );
/**
* Register a service by its name
*
* This method is intended to be called by modules for registering
* services. A module may register multiple services.
* The registry gain ownership of services.
*
* @param name the name of the service
* @param service a QgsServerResponse to be registered
* @param version the service version
*/
void registerService( const QString& name, QgsService* service /Transfer/, const QString& version );
/**
* Initialize registry, load modules and auto register services
* @param nativeModulepath the native module path
* @param pythonModulePath the python module path
*
* If pythonModulePath is not specified the environnement variables QGIS_PYTHON_SERVICE_PATH
* is examined.
*/
void init( const QString& nativeModulepath, const QString& pythonModulePath = QString() );
/**
* Clean up registered service and unregister modules
*/
void cleanUp();
};

View File

@ -28,3 +28,10 @@
%Include qgswfsprojectparser.sip
%Include qgsconfigcache.sip
%Include qgsserver.sip
%Include qgsserverrequest.sip
%Include qgsserverresponse.sip
%Include qgsservice.sip
%Include qgsservicemodule.sip
%Include qgsserviceregistry.sip

View File

@ -49,6 +49,16 @@ SET ( qgis_mapserv_SRCS
qgssldconfigparser.cpp
qgsconfigparserutils.cpp
qgsserver.cpp
#XXX https://github.com/qgis/QGIS-Enhancement-Proposals/issues/74
qgsservice.cpp
qgsservicemodule.cpp
qgsserviceloader.cpp
qgsservicenativeloader.cpp
qgsservicepythonloader.cpp
qgsserviceregistry.cpp
qgsserverrequest.cpp
qgsserverresponse.cpp
#----------------------------
)
IF("${Qt5Network_VERSION}" VERSION_LESS "5.0.0")
SET (qgis_mapserv_SRCS ${qgis_mapserv_SRCS}

View File

@ -0,0 +1,58 @@
/***************************************************************************
qgsserverrequest.cpp
Define ruquest class for getting request contents
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsserverrequest.h"
QgsServerRequest::QgsServerRequest( const QString& url, Method method )
: mUrl(url)
, mMethod(method)
{
}
QgsServerRequest::QgsServerRequest( const QUrl& url, Method method )
: mUrl(url)
, mMethod(method)
{
}
//! destructor
QgsServerRequest::~QgsServerRequest()
{
}
const QUrl& QgsServerRequest::url() const
{
return mUrl;
}
QgsServerRequest::Method QgsServerRequest::method() const
{
return mMethod;
}
const QByteArray* QgsServerRequest::data() const
{
return nullptr;
}

View File

@ -0,0 +1,83 @@
/***************************************************************************
qgsserverrequest.h
Define ruquest class for getting request contents
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSSERVERREQUEST_H
#define QGSSERVERREQUEST_H
#include <QUrl>
/**
* \ingroup server
* QgsServerRequest
* Class defining request intreface passed to services QgsService::executeRequest() method
*
* Note about design: this intreface must be passed along to python and thus signatures methods must be
* compatible with pyQGS/pyQT api and rules.
*/
class SERVER_EXPORT QgsServerRequest
{
public:
enum Method {
HeadMethod, PutMethod, GetMethod, PostMethod, DeleteMethod
};
/**
* Constructor
*
* @param url the lurl string
* @param method the request method
*/
QgsServerRequest( const QString& url, Method method );
/**
* Constructor
*
* @param url QUrl
* @param method the rquest method
*/
QgsServerRequest( const QUrl& url, Method method );
//! destructor
virtual ~QgsServerRequest();
/**
* @return the request url
*/
virtual const QUrl& url() const;
/**
* @return the rquest method
*/
virtual Method method() const;
/**
* Return post/put data
* The default implementation retfurn nullptr
* @return a QByteArray pointer or nullptr
*/
virtual const QByteArray* data() const;
protected:
QUrl mUrl;
Method mMethod;
};
#endif

View File

@ -0,0 +1,82 @@
/***************************************************************************
qgsserverresponse.h
Define response class for services
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsserverresponse.h"
#include "qgsmessagelog.h"
#include <QTextStream>
//! constructor
QgsServerResponse::QgsServerResponse()
{
}
//! destructor
QgsServerResponse::~QgsServerResponse()
{
}
void QgsServerResponse::write( const QString& data )
{
QIODevice* iodev = io();
if( iodev )
{
QTextStream stream(iodev);
stream << data;
}
else
{
QgsMessageLog::logMessage("Error: No IODevice in QgsServerResponse !!!");
}
}
qint64 QgsServerResponse::write( const QByteArray& byteArray )
{
QIODevice* iodev = io();
if( iodev )
{
return iodev->write(byteArray);
}
return 0;
}
qint64 QgsServerResponse::write( const char* data, qint64 maxsize )
{
QIODevice* iodev = io();
if( iodev )
{
return iodev->write(data, maxsize);
}
return 0;
}
qint64 QgsServerResponse::write( const char* data )
{
QIODevice* iodev = io();
if( iodev )
{
return iodev->write(data);
}
return 0;
}

View File

@ -0,0 +1,86 @@
/***************************************************************************
qgsserverresponse.h
Define response class for services
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSSERVERRESPONSE_H
#define QGSSERVERRESPONSE_H
#include <QString>
#include <QIODevice>
/**
* \ingroup server
* QgsServerResponse
* Class defining response interface passed to services QgsService::executeRequest() method
*
* Note:
* This class is intended to be used from python code: method signatures and return types should be
* compatible with pyQGIS/pyQT types and rules.
*
*/
class SERVER_EXPORT QgsServerResponse
{
public:
//!constructor
QgsServerResponse();
//! destructor
virtual ~QgsServerResponse();
/** Set header entry
* Add header entry to the response
* Note that it is usually an error to set hedaer after writng data
*/
virtual void setHeader( const QString& key, const QString& value ) = 0;
/** Set the http return code
*/
virtual void setReturnCode( int code ) = 0;
/**
* Send error
*/
virtual void sendError( int code, const QString& message ) = 0;
/**
* Write string
* @param data string to write
*/
virtual void write( const QString& data );
/**
* Write chunk af data
* They are convenience method that will write directly to the
* underlying I/O device
*/
virtual qint64 write( const QByteArray &byteArray );
// Not exposed in python
virtual qint64 write( const char* data, qint64 maxsize);
// Not exposed in python
virtual qint64 write( const char* data );
/**
* Return the underlying QIODevice
*/
virtual QIODevice* io() = 0;
};
#endif

35
src/server/qgsservice.cpp Normal file
View File

@ -0,0 +1,35 @@
/***************************************************************************
qgsservice.cpp
Class defining the service interface for QGIS server services.
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsservice.h"
//! Constructor
QgsService::QgsService()
{
}
//! Destructor
QgsService::~QgsService()
{
}

64
src/server/qgsservice.h Normal file
View File

@ -0,0 +1,64 @@
/***************************************************************************
qgsservice.h
Class defining the service interface for QGIS server services.
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSSERVICECOMPONENT_H
#define QGSSERVICECOMPONENT_H
#include "qgsserverrequest.h"
#include "qgsserverresponse.h"
/**
* \ingroup server
* QgsService
* Class defining interfaces for QGIS server services
*
* This class provides methods for executing server requests
* They are registered at runtime for a given service name.
*
*/
class SERVER_EXPORT QgsService
{
public:
//! Constructor
QgsService();
//! Destructor
virtual ~QgsService();
/**
* Return true if the given method is supported for that
* service.
* @param method QGSMethodType the
* @return QString containing the configuration file path
*/
virtual bool allowMethod( QgsServerRequest::Method ) const = 0;
/**
* Execute the requests and set result in QgsServerRequest
* @param request a QgsServerRequest instance
* @param response a QgsServerResponse instance
*/
virtual void executeRequest( const QgsServerRequest& request, QgsServerResponse& response ) = 0;
};
#endif

View File

@ -0,0 +1,34 @@
/***************************************************************************
qgsserviceloader.cpp
Define abstract loader class for service modules
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsserviceloader.h"
//! Constructor
QgsServiceLoader::QgsServiceLoader()
{
}
//! Destructor
QgsServiceLoader::~QgsServiceLoader()
{
}

View File

@ -0,0 +1,57 @@
/***************************************************************************
qgsserviceloader.h
Define abstract loader class for service modules
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSSERVICELOADER_H
#define QGSSERVICELOADER_H
#include <QString>
class QgsServiceModule;
class QgsServiceRegistry;
/**
* \ingroup server
* QgsServiceLoader
* Abstract base Class defining the native service module loader for QGIS server services
*/
class SERVER_EXPORT QgsServiceLoader
{
public:
//! Constructor (required by SIP bindings ???)
QgsServiceLoader();
//! Destructor
virtual ~QgsServiceLoader() = 0;
/**
* Lead all medules from path
* @param modulePath the path to look for module
* @param registrar QgsServiceRegistry instance for registering services
*/
virtual void loadModules( const QString& modulePath, QgsServiceRegistry& registrar ) = 0;
/**
* Unload all modules
*/
virtual void unloadModules() = 0;
};
#endif

View File

@ -0,0 +1,36 @@
/***************************************************************************
qgsservicemodule.h
Class defining the service module interface for QGIS server services.
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsservicemodule.h"
//! Constructor
QgsServiceModule::QgsServiceModule()
{
}
//! Destructor
QgsServiceModule::~QgsServiceModule()
{
}

View File

@ -0,0 +1,54 @@
/***************************************************************************
qgsservicemodule.h
Class defining the service module interface for QGIS server services.
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSSERVICEMODULE_H
#define QGSSERVICEMODULE_H
class QgsServiceRegistry;
/**
* \ingroup server
* QgsServiceModule
* Class defining the service module interface for QGIS server services
*
* This class act as a service registrar for services.
*
* For dynamic modules, a QgsServiceModule instance is returned from the QGS_ServiceModule_Init() entry point
*/
class SERVER_EXPORT QgsServiceModule
{
public:
//! Constructor
QgsServiceModule();
//! Destructor
virtual ~QgsServiceModule() = 0;
/**
* Ask module to register all provided services
* @param registry QgsServiceRegistry
*/
virtual void registerSelf( QgsServiceRegistry& registry ) = 0;
};
#endif

View File

@ -0,0 +1,168 @@
/***************************************************************************
qgsservicerenativeloader.cpp
Define Loader for native service modules
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <QLibrary>
#include <QDir>
#include "qgsservicenativeloader.h"
#include "qgsserviceregistry.h"
#include "qgsservicemodule.h"
#include "qgslogger.h"
#include "qgsmessagelog.h"
#include "qgis.h"
typedef void unloadHook_t( QgsServiceModule* );
class QgsServiceNativeModuleEntry
{
public:
QgsServiceNativeModuleEntry( const QString& location )
: mLocation(location)
, mModule(nullptr)
{}
QString mLocation;
QgsServiceModule* mModule;
unloadHook_t* mUnloadHook;
};
//! Constructor
QgsServiceNativeLoader::QgsServiceNativeLoader()
{
}
//! Destructor
QgsServiceNativeLoader::~QgsServiceNativeLoader()
{
}
void QgsServiceNativeLoader::loadModules( const QString& modulePath, QgsServiceRegistry& registrar )
{
QDir moduleDir(modulePath);
moduleDir.setSorting( QDir::Name | QDir::IgnoreCase );
moduleDir.setFilter( QDir::Files );
#if defined(Q_OS_WIN) || defined(__CYGWIN__)
moduleDir.setNameFilters( QStringList( "*.dll" ) );
#else
moduleDir.setNameFilters( QStringList( "*.so" ) );
#endif
QgsDebugMsg( QString( "Checking %1 for native services modules" ).arg( moduleDir.path() ) );
Q_FOREACH( const QFileInfo& fi, moduleDir.entryInfoList() )
{
QgsServiceModule* module = loadNativeModule( fi.filePath() );
if( module )
{
// Register services
module->registerSelf( registrar );
}
}
}
typedef QgsServiceModule* serviceEntryPoint_t();
QgsServiceModule* QgsServiceNativeLoader::loadNativeModule( const QString& location )
{
QgsServiceNativeModuleEntry* entry = findModuleEntry( location );
if( entry )
{
return entry->mModule;
}
QLibrary lib( location );
QgsDebugMsg( QString("Loading native module %1").arg(location) );
if( !lib.load() )
{
QgsMessageLog::logMessage( QString("Failed to load library %1: %2").arg(lib.fileName(), lib.errorString()) );
return nullptr;
}
// Load entry point
serviceEntryPoint_t*
entryPointFunc = reinterpret_cast<serviceEntryPoint_t*>( cast_to_fptr( lib.resolve("QGS_ServiceModule_Init") ));
if( entryPointFunc )
{
QgsServiceModule* module = entryPointFunc();
if( module )
{
entry = new QgsServiceNativeModuleEntry( location );
entry->mModule = module;
entry->mUnloadHook = reinterpret_cast<unloadHook_t*>( cast_to_fptr( lib.resolve("QGS_ServiceModule_Exit") ));
// Add entry
mModules.insert( location, ModuleTable::mapped_type(entry) );
return module;
}
else
{
QgsMessageLog::logMessage( QString("No entry point for module %1").arg(lib.fileName()) );
}
}
else
{
QgsMessageLog::logMessage( QString("Error: entry point returned null for %1").arg(lib.fileName()) );
}
// No module found: release library
lib.unload();
return nullptr;
}
void QgsServiceNativeLoader::unloadModules()
{
ModuleTable::iterator it = mModules.begin();
ModuleTable::iterator end = mModules.end();
while( it!=end )
{
unloadModuleEntry( it->get() );
++it;
}
mModules.clear();
}
QgsServiceNativeModuleEntry* QgsServiceNativeLoader::findModuleEntry( const QString& location )
{
QgsServiceNativeModuleEntry* entry = nullptr;
ModuleTable::iterator item = mModules.find(location);
if( item != mModules.end() )
{
entry = item->get();
}
return entry;
}
void QgsServiceNativeLoader::unloadModuleEntry( QgsServiceNativeModuleEntry* entry )
{
// Call cleanup function if it exists
if( entry->mUnloadHook ) {
entry->mUnloadHook( entry->mModule );
}
QLibrary lib( entry->mLocation );
lib.unload();
}

View File

@ -0,0 +1,90 @@
/***************************************************************************
qgsservicerenativeloader.h
Define Loader for native service modules
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSSERVICENATIVELOADER_H
#define QGSSERVICENATIVELOADER_H
class QgsServiceModule;
class QgsServiceRegistry;
class QgsServiceNativeModuleEntry;
#include "qgsserviceloader.h"
#include <QHash>
#include <memory>
/**
* \ingroup server
* QgsServiceNativeLoader
* Class defining the native service module loader for QGIS server services
*
* This class provides methods for loading and managing hook for native (C++) modules
*
*/
class SERVER_EXPORT QgsServiceNativeLoader: public QgsServiceLoader
{
public:
//! Constructor
QgsServiceNativeLoader();
//! Destructor
~QgsServiceNativeLoader();
/**
* Lead all medules from path
* @param modulePath the path to look for module
* @parama registrar QgsServiceRegistry instance for registering services
*/
void loadModules( const QString& modulePath, QgsServiceRegistry& registrar ) override;
/**
* Unload all modules
*/
void unloadModules() override;
/**
* Load the native module from path
*
* @param location QString location holding the module relalive path
* @return a qgsservicemodule instance
*/
QgsServiceModule* loadNativeModule( const QString& location );
private:
typedef QHash<QString, std::shared_ptr<QgsServiceNativeModuleEntry> > ModuleTable;
/**
* Find module
* @param path the module path
* @return a module hook entry
*/
QgsServiceNativeModuleEntry* findModuleEntry( const QString& path );
/**
* Unload medule hook
*/
void unloadModuleEntry( QgsServiceNativeModuleEntry* entry );
//! Associative storage for module handles
ModuleTable mModules;
};
#endif

View File

@ -0,0 +1,82 @@
/***************************************************************************
qgsservicerenativeloader.cpp
Define Loader for native service modules
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsservicepythonloader.h"
#include "qgsserviceregistry.h"
#include "qgsmessagelog.h"
#include <QLibrary>
#include <QDir>
typedef void unloadHook_t( QgsServiceModule* );
class QgsServicePythonModuleEntry
{
public:
QgsServicePythonModuleEntry( const QString& location )
: mLocation(location)
, mModule(nullptr)
{}
QString mLocation;
QgsServiceModule* mModule;
unloadHook_t* mUnloadHook;
};
//! Constructor
QgsServicePythonLoader::QgsServicePythonLoader()
{
}
//! Destructor
QgsServicePythonLoader::~QgsServicePythonLoader()
{
}
void QgsServicePythonLoader::loadModules( const QString& modulePath, QgsServiceRegistry& registrar )
{
//TODO
}
void QgsServicePythonLoader::unloadModules()
{
ModuleTable::iterator it = mModules.begin();
ModuleTable::iterator end = mModules.end();
while( it!=end )
{
unloadModuleEntry( it->get() );
++it;
}
mModules.clear();
}
void QgsServicePythonLoader::unloadModuleEntry( QgsServicePythonModuleEntry* entry )
{
// Call cleanup function if it exists
if( entry->mUnloadHook ) {
entry->mUnloadHook( entry->mModule );
}
}

View File

@ -0,0 +1,73 @@
/***************************************************************************
qgsservicepythonloader.h
Define Loader for python service modules
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSSERVICEPYTHONLOADER_H
#define QGSSERVICEPYTHONLOADER_H
class QgsServiceModule;
class QgsServicePythonModuleEntry;
class QgsServiceRegistry;
#include "qgsserviceloader.h"
#include <QHash>
#include <memory>
/**
* \ingroup server
* QgsServicePythonLoader
* Class defining the python service module loader for QGIS server services
*
* This class provides methods for loading and managing hook for native (C++) modules
*
*/
class SERVER_EXPORT QgsServicePythonLoader: public QgsServiceLoader
{
public:
//! Constructor
QgsServicePythonLoader();
//! Destructor
~QgsServicePythonLoader();
/**
* Lead all medules from path
* @param modulePath the path to look for module
* @parama registrar QgsServiceRegistry instance for registering services
*/
void loadModules( const QString& modulePath, QgsServiceRegistry& registrar ) override;
/**
* Unload all modules
*/
void unloadModules() override;
private:
typedef QHash<QString, std::shared_ptr<QgsServicePythonModuleEntry> > ModuleTable;
/**
* Unload medule hook
*/
void unloadModuleEntry( QgsServicePythonModuleEntry* entry );
//! Associative storage for module handles
ModuleTable mModules;
};
#endif

View File

@ -0,0 +1,185 @@
/***************************************************************************
qgsserviceregistry.cpp
Class defining the service manager for QGIS server services.
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsserviceregistry.h"
#include "qgsservice.h"
#include "qgslogger.h"
#include "qgsmessagelog.h"
namespace {
// Build a key entry from name and version
QString make_service_key( const QString& name, const QString& version )
{
return QString( "%1_%2" ).arg(name,version);
}
// Compare two version strings:
// The strings are splitted into dot separated segment
// Each segment are compared up to the shortest number of segment of the
// lists. Remaining segments are dropped.
// If both segments can be intepreted as numbers the are compared as numbers, otherwise
// They are compared lexicographically.
// Return true if v1 is greater than v2
bool is_version_greater( const QString& v1, const QString& v2 )
{
QStringList l1 = v1.split('.');
QStringList l2 = v2.split('.');
QStringList::iterator it1 = l1.begin();
QStringList::iterator it2 = l2.begin();
bool isint;
while( it1 != l1.end() && it2 != l2.end() )
{
if ( *it1 != *it2 )
{
// Compare as numbers
int i1 = it1->toInt(&isint);
if(isint)
{
int i2 = it2->toInt(&isint);
if( isint && i1 != i2 )
{
return i1 > i2;
}
}
// Compare lexicographically
if ( !isint )
{
return *it1 > *it2;
}
}
++it1;
++it2;
}
// We reach the end of one of the list
return false;
}
} // namespace
class QgsServiceEntry
{
public:
~QgsServiceEntry()
{
// We have the ownership by design
// XXX Take care of /Transfer/ decorator for registerService in sip
QgsDebugMsg( QString("Deleting service %1 %2").arg(mName, mVersion) );
delete mService;
}
QgsServiceEntry( const QString& name, QgsService* service, const QString& version )
: mName(name)
, mService(service)
, mVersion(version)
{}
QString mName;
QgsService* mService;
QString mVersion;
};
QgsServiceRegistry::QgsServiceRegistry()
{
//TODO
}
QgsServiceRegistry::~QgsServiceRegistry()
{
cleanUp();
}
QgsService* QgsServiceRegistry::getService( const QString& name, const QString& version )
{
QgsService* service = nullptr;
QString key;
// Check that we have a service of that name
VersionTable::iterator v = mVersions.find(name);
if( v != mVersions.end() )
{
key = version.isEmpty() ? v->second : make_service_key(name, version );
ServiceTable::iterator it = mServices.find(key);
if( it != mServices.end() )
{
service = (*it)->mService;
}
else
{
QgsMessageLog::logMessage( QString("Service %1 %2 not found").arg(name, version) );
}
}
else
{
QgsMessageLog::logMessage( QString("Service %1 is not registered").arg(name) );
}
return service;
}
void QgsServiceRegistry::registerService( const QString& name, QgsService* service, const QString& version )
{
// Test if service is already registered
QString key = make_service_key( name, version );
if( mServices.find(key) != mServices.end() )
{
QgsMessageLog::logMessage( QString("Error Service %1 %2 is already registered").arg(name,version) );
return;
}
QgsMessageLog::logMessage( QString( "Adding service %1 %2").arg(name,version) );
mServices.insert( key, std::make_shared<QgsServiceEntry>( name, service, version ) );
// Check the default version
// and replace with te new one if it has a higher version
VersionTable::iterator v = mVersions.find( name );
if( v != mVersions.end() )
{
if( is_version_greater( version, v->first ) )
{
// Replace the default version key
mVersions.insert( name, VersionTable::mapped_type( version, key ) );
}
}
else
{
// Insert the service as the default one
mVersions.insert( name, VersionTable::mapped_type( version, key ) );
}
}
void QgsServiceRegistry::init( const QString& nativeModulePath, const QString& pythonModulePath )
{
mNativeLoader.loadModules(nativeModulePath, *this );
#ifdef HAVE_SERVER_PYTHON_SERVICES
mPythonLoader.loadModules(pythonModulePath, *this );
#endif
}
void QgsServiceRegistry::cleanUp()
{
// Release all services
mServices.clear();
mNativeLoader.unloadModules();
#ifdef HAVE_SERVER_PYTHON_SERVICES
mPythonLoader.unloadModules();
#endif
}

View File

@ -0,0 +1,105 @@
/***************************************************************************
qgsserviceregstry.h
Class defining the service manager for QGIS server services.
-------------------
begin : 2016-12-05
copyright : (C) 2016 by David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSSERVICEREGISTRY_H
#define QGSSERVICEREGISTRY_H
#include "qgsconfig.h"
#include <QHash>
#include <QString>
#include "qgsservicenativeloader.h"
#include "qgsservicepythonloader.h"
#include <memory>
class QgsService;
class QgsServiceEntry;
/**
* \ingroup server
* QgsServiceRegistry
* Class defining the cegistry manager for QGIS server services
*
* This class provides methods for registering and retrieving
* services. Note that the regstiry does not hord ownership of
* registered service but vill call the 'release' method on cleanup)
*
*/
class SERVER_EXPORT QgsServiceRegistry
{
public:
//! Constructor
QgsServiceRegistry();
//! Destructor
~QgsServiceRegistry();
/**
* Retrieve a service from its name
* @param name the name of the service
* @param version the version string (optional)
* @return QgsService
*
* If the version is not provided the higher version of the service is rerturnod
*/
QgsService* getService( const QString& name, const QString& version = QString() );
/**
* Register a service by its name
*
* This method is intended to be called by modules for registering
* services. A module may register multiple services.
* The registry gain ownership of services.
*
* @param name the name of the service
* @param service a QgsServerResponse to be registered
* @param version the version string for the service (required)
*/
void registerService( const QString& name, QgsService* service, const QString& version );
/**
* Initialize registry, load modules and auto register services
* @param nativeModulepath the native module path
* @param pythonModulePath the python module path
*
* If pythonModulePath is not specified the environnement variables QGIS_PYTHON_SERVICE_PATH
* is examined.
*/
void init( const QString& nativeModulepath, const QString& pythonModulePath = QString() );
/**
* Clean up registered service and unregister modules
*/
void cleanUp();
private:
typedef QHash<QString, std::shared_ptr<QgsServiceEntry> > ServiceTable;
typedef QHash<QString, QPair<QString, QString> > VersionTable;
QgsServiceNativeLoader mNativeLoader;
QgsServicePythonLoader mPythonLoader;
ServiceTable mServices;
VersionTable mVersions;
};
#endif

View File

@ -172,4 +172,5 @@ IF (WITH_SERVER)
ADD_PYTHON_TEST(PyQgsAuthManagerPasswordOWSTest test_authmanager_password_ows.py)
ADD_PYTHON_TEST(PyQgsAuthManagerPKIOWSTest test_authmanager_pki_ows.py)
ADD_PYTHON_TEST(PyQgsAuthManagerPKIPostgresTest test_authmanager_pki_postgres.py)
ADD_PYTHON_TEST(PyQgsServicesTest test_services.py)
ENDIF (WITH_SERVER)

View File

@ -0,0 +1,89 @@
""" QGIS test for server services
"""
from qgis.PyQt.QtCore import QBuffer, QIODevice, QTextStream
from qgis.testing import unittest
from qgis.server import (QgsServiceRegistry,
QgsService,
QgsServerRequest,
QgsServerResponse)
class Response(QgsServerResponse):
def __init__( self ):
QgsServerResponse.__init__(self)
self._buffer = QBuffer()
self._buffer.open(QIODevice.ReadWrite)
def setReturnCode( self, code ):
pass
def setHeader( self, key, val ):
pass
def sendError( self, code, message ):
pass
def io(self):
return self._buffer
class MyService(QgsService):
def __init__(self, response):
QgsService.__init__(self)
self._response = response
def executeRequest( self, request, response ):
url = request.url()
response.setReturnCode(201)
response.write(self._response)
class TestServices(unittest.TestCase):
"""
"""
def test_register(self):
reg = QgsServiceRegistry()
myserv = MyService("Hello world")
reg.registerService("STUFF", myserv, "1.0" )
# Retrieve service
request = QgsServerRequest("http://DoStufff", QgsServerRequest.GetMethod)
response = Response()
service = reg.getService("STUFF")
if service:
service.executeRequest(request, response)
io = response.io();
io.seek(0)
self.assertEquals(QTextStream(io).readLine(), "Hello world")
def test_version_registration(self):
reg = QgsServiceRegistry()
myserv1 = MyService("1.0")
myserv2 = MyService("1.1")
reg.registerService("STUFF", myserv1, myserv1._response)
reg.registerService("STUFF", myserv2, myserv2._response)
service = reg.getService("STUFF")
self.assertIsNotNone(service)
self.assertEquals(service._response, myserv2._response)
service = reg.getService("STUFF", "2.0")
self.assertIsNone(service)
if __name__ == '__main__':
unittest.main()