QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
Loading...
Searching...
No Matches
qgsserviceregistry.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsserviceregistry.cpp
3
4 Class defining the service manager for QGIS server services.
5 -------------------
6 begin : 2016-12-05
7 copyright : (C) 2016 by David Marteau
8 email : david dot marteau at 3liz dot com
9 ***************************************************************************/
10
11/***************************************************************************
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 * *
18 ***************************************************************************/
19
20#include "qgsserviceregistry.h"
21
22#include <algorithm>
23#include <functional>
24
25#include "qgsmessagelog.h"
26#include "qgsserverapi.h"
27#include "qgsservice.h"
28
29#include <QString>
30
31using namespace Qt::StringLiterals;
32
33namespace
34{
35
36 // Build a key entry from name and version
37 QString makeServiceKey( const QString &name, const QString &version )
38 {
39 return QString( "%1_%2" ).arg( name, version );
40 }
41
42 // Compare two version strings:
43 // The strings are split into dot separated segment
44 // Each segment are compared up to the shortest number of segment of the
45 // lists. Remaining segments are dropped.
46 // If both segments can be interpreted as numbers the are compared as numbers, otherwise
47 // They are compared lexicographically.
48 // Return true if v1 is greater than v2
49 bool isVersionGreater( const QString &v1, const QString &v2 )
50 {
51 QStringList l1 = v1.split( '.' );
52 QStringList l2 = v2.split( '.' );
53 QStringList::iterator it1 = l1.begin();
54 QStringList::iterator it2 = l2.begin();
55 bool isint;
56 while ( it1 != l1.end() && it2 != l2.end() )
57 {
58 if ( *it1 != *it2 )
59 {
60 // Compare as numbers
61 const int i1 = it1->toInt( &isint );
62 if ( isint )
63 {
64 const int i2 = it2->toInt( &isint );
65 if ( isint && i1 != i2 )
66 {
67 return i1 > i2;
68 }
69 }
70 // Compare lexicographically
71 if ( !isint )
72 {
73 return *it1 > *it2;
74 }
75 }
76 ++it1;
77 ++it2;
78 }
79 // We reach the end of one of the list
80 return false;
81 }
82
83 // Check that two versions are c
84
85
86} // namespace
87
92
93QgsService *QgsServiceRegistry::getService( const QString &name, const QString &version )
94{
95 QgsService *service = nullptr;
96 QString key;
97
98 // Check that we have a service of that name
99 const VersionTable::const_iterator v = mServiceVersions.constFind( name );
100 if ( v != mServiceVersions.constEnd() )
101 {
102 key = version.isEmpty() ? v->second : makeServiceKey( name, version );
103 const ServiceTable::const_iterator it = mServices.constFind( key );
104 if ( it != mServices.constEnd() )
105 {
106 service = it->get();
107 }
108 else
109 {
110 // Return the default version
111 QgsMessageLog::logMessage( QString( "Service %1 %2 not found, returning default" ).arg( name, version ), u"Server"_s, Qgis::MessageLevel::Warning );
112 service = mServices[v->second].get();
113 }
114 }
115 else
116 {
117 QgsMessageLog::logMessage( QString( "Service %1 is not registered" ).arg( name ), u"Server"_s, Qgis::MessageLevel::Critical );
118 }
119 return service;
120}
121
123{
124 const QString name = service->name();
125 const QString version = service->version();
126
127 // Test if service is already registered
128 const QString key = makeServiceKey( name, version );
129 if ( mServices.constFind( key ) != mServices.constEnd() )
130 {
131 QgsMessageLog::logMessage( u"Error Service %1 %2 is already registered"_s.arg( name, version ), u"Server"_s, Qgis::MessageLevel::Critical );
132 return;
133 }
134
135 QgsMessageLog::logMessage( u"Adding service %1 %2"_s.arg( name, version ), u"Server"_s, Qgis::MessageLevel::Info );
136 mServices.insert( key, std::shared_ptr<QgsService>( service ) );
137
138 // Check the default version
139 // The first inserted service of a given name
140 // is the default one.
141 // this will ensure that native services are always
142 // the defaults.
143 const VersionTable::const_iterator v = mServiceVersions.constFind( name );
144 if ( v == mServiceVersions.constEnd() )
145 {
146 // Insert the service as the default one
147 mServiceVersions.insert( name, VersionTable::mapped_type( version, key ) );
148 }
149 /*
150 if ( v != mVersions.constEnd() )
151 {
152 if ( isVersionGreater( version, v->first ) )
153 {
154 // Replace the default version key
155 mVersions.insert( name, VersionTable::mapped_type( version, key ) );
156 }
157 }
158 else
159 {
160 // Insert the service as the default one
161 mVersions.insert( name, VersionTable::mapped_type( version, key ) );
162 }*/
163}
164
165int QgsServiceRegistry::unregisterApi( const QString &name, const QString &version )
166{
167 // Check that we have an API of that name
168 int removed = 0;
169 const VersionTable::const_iterator v = mApiVersions.constFind( name );
170 if ( v != mApiVersions.constEnd() )
171 {
172 if ( version.isEmpty() )
173 {
174 // No version specified, remove all versions
175 ApiTable::iterator it = mApis.begin();
176 while ( it != mApis.end() )
177 {
178 if ( ( *it )->name() == name )
179 {
180 QgsMessageLog::logMessage( QString( "Unregistering API %1 %2" ).arg( name, ( *it )->version() ), u"Server"_s, Qgis::MessageLevel::Info );
181 it = mApis.erase( it );
182 ++removed;
183 }
184 else
185 {
186 ++it;
187 }
188 }
189 // Remove from version table
190 mApiVersions.remove( name );
191 }
192 else
193 {
194 const QString key = makeServiceKey( name, version );
195 const ApiTable::iterator found = mApis.find( key );
196 if ( found != mApis.end() )
197 {
198 QgsMessageLog::logMessage( QString( "Unregistering API %1 %2" ).arg( name, version ), u"Server"_s, Qgis::MessageLevel::Info );
199 mApis.erase( found );
200 removed = 1;
201
202 // Find if we have other services of that name
203 // but with different version
204 //
205 QString maxVer;
206 const std::function<void( const ApiTable::mapped_type & )> findGreaterVersion = [name, &maxVer]( const ApiTable::mapped_type &api ) {
207 if ( api->name() == name && ( maxVer.isEmpty() || isVersionGreater( api->version(), maxVer ) ) )
208 maxVer = api->version();
209 };
210
211 mApiVersions.remove( name );
212
213 std::for_each( mApis.constBegin(), mApis.constEnd(), findGreaterVersion );
214 if ( !maxVer.isEmpty() )
215 {
216 // Set the new default service
217 const QString key = makeServiceKey( name, maxVer );
218 mApiVersions.insert( name, VersionTable::mapped_type( version, key ) );
219 }
220 }
221 }
222 }
223 return removed;
224}
225
227{
228 for ( const auto &api : mApis )
229 {
230 QgsMessageLog::logMessage( u"Trying URL path: '%1' for '%2'"_s.arg( request.url().path(), api->rootPath() ), u"Server"_s, Qgis::MessageLevel::Info );
231 if ( api->accept( request.url() ) )
232 {
233 Q_ASSERT( !api->name().isEmpty() );
234 QgsMessageLog::logMessage( u"API %1 accepts the URL path '%2' "_s.arg( api->name(), request.url().path() ), u"Server"_s, Qgis::MessageLevel::Info );
235 return api.get();
236 }
237 }
238 return nullptr;
239}
240
241QgsServerApi *QgsServiceRegistry::getApi( const QString &name, const QString &version )
242{
243 QgsServerApi *api = nullptr;
244 QString key;
245
246 // Check that we have an API with that name
247 const VersionTable::const_iterator v = mApiVersions.constFind( name );
248 if ( v != mApiVersions.constEnd() )
249 {
250 key = version.isEmpty() ? v->second : makeServiceKey( name, version );
251 const ApiTable::const_iterator it = mApis.constFind( key );
252 if ( it != mApis.constEnd() )
253 {
254 api = it->get();
255 }
256 else
257 {
258 // Return the default version
259 QgsMessageLog::logMessage( QString( "API %1 %2 not found, returning default" ).arg( name, version ), u"Server"_s, Qgis::MessageLevel::Warning );
260 api = mApis[v->second].get();
261 }
262 }
263 else
264 {
265 QgsMessageLog::logMessage( QString( "API %1 is not registered" ).arg( name ), u"Server"_s, Qgis::MessageLevel::Critical );
266 }
267 return api;
268}
269
270int QgsServiceRegistry::unregisterService( const QString &name, const QString &version )
271{
272 // Check that we have a service of that name
273 int removed = 0;
274 const VersionTable::const_iterator v = mServiceVersions.constFind( name );
275 if ( v != mServiceVersions.constEnd() )
276 {
277 if ( version.isEmpty() )
278 {
279 // No version specified, remove all versions
280 ServiceTable::iterator it = mServices.begin();
281 while ( it != mServices.end() )
282 {
283 if ( ( *it )->name() == name )
284 {
285 QgsMessageLog::logMessage( QString( "Unregistering service %1 %2" ).arg( name, ( *it )->version() ), u"Server"_s, Qgis::MessageLevel::Info );
286 it = mServices.erase( it );
287 ++removed;
288 }
289 else
290 {
291 ++it;
292 }
293 }
294 // Remove from version table
295 mServiceVersions.remove( name );
296 }
297 else
298 {
299 const QString key = makeServiceKey( name, version );
300 const ServiceTable::iterator found = mServices.find( key );
301 if ( found != mServices.end() )
302 {
303 QgsMessageLog::logMessage( QString( "Unregistering service %1 %2" ).arg( name, version ), u"Server"_s, Qgis::MessageLevel::Info );
304 mServices.erase( found );
305 removed = 1;
306
307 // Find if we have other services of that name
308 // but with different version
309 //
310 QString maxVer;
311 const std::function<void( const ServiceTable::mapped_type & )> findGreaterVersion = [name, &maxVer]( const ServiceTable::mapped_type &service ) {
312 if ( service->name() == name && ( maxVer.isEmpty() || isVersionGreater( service->version(), maxVer ) ) )
313 maxVer = service->version();
314 };
315
316 mServiceVersions.remove( name );
317
318 std::for_each( mServices.constBegin(), mServices.constEnd(), findGreaterVersion );
319 if ( !maxVer.isEmpty() )
320 {
321 // Set the new default service
322 const QString key = makeServiceKey( name, maxVer );
323 mServiceVersions.insert( name, VersionTable::mapped_type( version, key ) );
324 }
325 }
326 }
327 }
328 return removed;
329}
330
331void QgsServiceRegistry::init( const QString &nativeModulePath, QgsServerInterface *serverIface )
332{
333 mNativeLoader.loadModules( nativeModulePath, *this, serverIface );
334}
335
337{
338 // Release all services
339 mServiceVersions.clear();
340 mServices.clear();
341 mApis.clear();
342 mNativeLoader.unloadModules();
343}
344
346{
347 const QString name = api->name();
348 const QString version = api->version();
349
350 // Test if service is already registered
351 const QString key = makeServiceKey( name, version );
352 if ( mApis.constFind( key ) != mApis.constEnd() )
353 {
354 QgsMessageLog::logMessage( u"Error API %1 %2 is already registered"_s.arg( name, version ), u"Server"_s, Qgis::MessageLevel::Critical );
355 return false;
356 }
357
358 QgsMessageLog::logMessage( u"Adding API %1 %2 - root path: %3"_s.arg( name, version, api->rootPath() ), QString(), Qgis::MessageLevel::Info );
359 mApis.insert( key, std::shared_ptr<QgsServerApi>( api ) );
360
361 // Check the default version
362 // The first inserted service of a given name
363 // is the default one.
364 // this will ensure that native services are always
365 // the defaults.
366 const VersionTable::const_iterator v = mApiVersions.constFind( name );
367 if ( v == mApiVersions.constEnd() )
368 {
369 // Insert the service as the default one
370 mApiVersions.insert( name, VersionTable::mapped_type( version, key ) );
371 }
372 return true;
373}
@ Warning
Warning message.
Definition qgis.h:162
@ Critical
Critical/error message.
Definition qgis.h:163
@ Info
Information message.
Definition qgis.h:161
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).
Server generic API endpoint abstract base class.
virtual const QString version() const
Returns the version of the service.
virtual const QString name() const =0
Returns the API name.
virtual const QString rootPath() const =0
Returns the root path for the API.
Defines interfaces exposed by QGIS Server and made available to plugins.
Defines requests passed to QgsService classes.
QUrl url() const
Returns the request URL as seen by QGIS server.
void cleanUp()
Clean up registered service and unregister modules.
bool registerApi(QgsServerApi *api)
Registers the QgsServerApi api.
int unregisterService(const QString &name, const QString &version=QString())
Unregister service from its name and version.
int unregisterApi(const QString &name, const QString &version=QString())
Unregisters API from its name and version.
QgsServerApi * apiForRequest(const QgsServerRequest &request) const
Searches the API register for an API matching the request and returns a (possibly NULL) pointer to it...
void init(const QString &nativeModulepath, QgsServerInterface *serverIface=nullptr)
Initialize registry, load modules and auto register services.
QgsService * getService(const QString &name, const QString &version=QString())
Retrieve a service from its name.
void registerService(QgsService *service)
Register a service by its name and version.
QgsServerApi * getApi(const QString &name, const QString &version=QString())
Retrieves an API from its name.
Defines interfaces for QGIS server services.
Definition qgsservice.h:38
virtual QString name() const =0
Returns the name of the service.
virtual QString version() const =0
Returns the version of the service.