QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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#include "qgsservice.h"
22#include "qgsserverapi.h"
23#include "qgsmessagelog.h"
24
25#include <algorithm>
26#include <functional>
27
28
29namespace
30{
31
32// Build a key entry from name and version
33 QString makeServiceKey( const QString &name, const QString &version )
34 {
35 return QString( "%1_%2" ).arg( name, version );
36 }
37
38// Compare two version strings:
39// The strings are split into dot separated segment
40// Each segment are compared up to the shortest number of segment of the
41// lists. Remaining segments are dropped.
42// If both segments can be interpreted as numbers the are compared as numbers, otherwise
43// They are compared lexicographically.
44// Return true if v1 is greater than v2
45 bool isVersionGreater( const QString &v1, const QString &v2 )
46 {
47 QStringList l1 = v1.split( '.' );
48 QStringList l2 = v2.split( '.' );
49 QStringList::iterator it1 = l1.begin();
50 QStringList::iterator it2 = l2.begin();
51 bool isint;
52 while ( it1 != l1.end() && it2 != l2.end() )
53 {
54 if ( *it1 != *it2 )
55 {
56 // Compare as numbers
57 const int i1 = it1->toInt( &isint );
58 if ( isint )
59 {
60 const int i2 = it2->toInt( &isint );
61 if ( isint && i1 != i2 )
62 {
63 return i1 > i2;
64 }
65 }
66 // Compare lexicographically
67 if ( !isint )
68 {
69 return *it1 > *it2;
70 }
71 }
72 ++it1;
73 ++it2;
74 }
75 // We reach the end of one of the list
76 return false;
77 }
78
79// Check that two versions are c
80
81
82} // namespace
83
85{
86 cleanUp();
87}
88
89QgsService *QgsServiceRegistry::getService( const QString &name, const QString &version )
90{
91 QgsService *service = nullptr;
92 QString key;
93
94 // Check that we have a service of that name
95 const VersionTable::const_iterator v = mServiceVersions.constFind( name );
96 if ( v != mServiceVersions.constEnd() )
97 {
98 key = version.isEmpty() ? v->second : makeServiceKey( name, version );
99 const ServiceTable::const_iterator it = mServices.constFind( key );
100 if ( it != mServices.constEnd() )
101 {
102 service = it->get();
103 }
104 else
105 {
106 // Return the default version
107 QgsMessageLog::logMessage( QString( "Service %1 %2 not found, returning default" ).arg( name, version ),
108 QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
109 service = mServices[v->second].get();
110 }
111 }
112 else
113 {
114 QgsMessageLog::logMessage( QString( "Service %1 is not registered" ).arg( name ),
115 QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
116 }
117 return service;
118}
119
121{
122 const QString name = service->name();
123 const QString version = service->version();
124
125 // Test if service is already registered
126 const QString key = makeServiceKey( name, version );
127 if ( mServices.constFind( key ) != mServices.constEnd() )
128 {
129 QgsMessageLog::logMessage( QStringLiteral( "Error Service %1 %2 is already registered" ).arg( name, version ),
130 QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
131 return;
132 }
133
134 QgsMessageLog::logMessage( QStringLiteral( "Adding service %1 %2" ).arg( name, version ),
135 QStringLiteral( "Server" ), 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}
165
166int QgsServiceRegistry::unregisterApi( const QString &name, const QString &version )
167{
168
169 // Check that we have an API of that name
170 int removed = 0;
171 const VersionTable::const_iterator v = mApiVersions.constFind( name );
172 if ( v != mApiVersions.constEnd() )
173 {
174 if ( version.isEmpty() )
175 {
176 // No version specified, remove all versions
177 ApiTable::iterator it = mApis.begin();
178 while ( it != mApis.end() )
179 {
180 if ( ( *it )->name() == name )
181 {
182 QgsMessageLog::logMessage( QString( "Unregistering API %1 %2" ).arg( name, ( *it )->version() ),
183 QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
184 it = mApis.erase( it );
185 ++removed;
186 }
187 else
188 {
189 ++it;
190 }
191 }
192 // Remove from version table
193 mApiVersions.remove( name );
194 }
195 else
196 {
197 const QString key = makeServiceKey( name, version );
198 const ApiTable::iterator found = mApis.find( key );
199 if ( found != mApis.end() )
200 {
201 QgsMessageLog::logMessage( QString( "Unregistering API %1 %2" ).arg( name, version ),
202 QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
203 mApis.erase( found );
204 removed = 1;
205
206 // Find if we have other services of that name
207 // but with different version
208 //
209 QString maxVer;
210 const std::function < void ( const ApiTable::mapped_type & ) >
211 findGreaterVersion = [name, &maxVer]( const ApiTable::mapped_type & api )
212 {
213 if ( api->name() == name &&
214 ( maxVer.isEmpty() || isVersionGreater( api->version(), maxVer ) ) )
215 maxVer = api->version();
216 };
217
218 mApiVersions.remove( name );
219
220 std::for_each( mApis.constBegin(), mApis.constEnd(), findGreaterVersion );
221 if ( !maxVer.isEmpty() )
222 {
223 // Set the new default service
224 const QString key = makeServiceKey( name, maxVer );
225 mApiVersions.insert( name, VersionTable::mapped_type( version, key ) );
226 }
227 }
228 }
229 }
230 return removed;
231}
232
234{
235 for ( const auto &api : mApis )
236 {
237 QgsMessageLog::logMessage( QStringLiteral( "Trying URL path: '%1' for '%2'" ).arg( request.url().path(), api->rootPath() ),
238 QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
239 if ( api->accept( request.url() ) )
240 {
241 Q_ASSERT( !api->name().isEmpty() );
242 QgsMessageLog::logMessage( QStringLiteral( "API %1 accepts the URL path '%2' " ).arg( api->name(), request.url().path() ),
243 QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
244 return api.get();
245 }
246 }
247 return nullptr;
248}
249
250QgsServerApi *QgsServiceRegistry::getApi( const QString &name, const QString &version )
251{
252 QgsServerApi *api = nullptr;
253 QString key;
254
255 // Check that we have an API with that name
256 const VersionTable::const_iterator v = mApiVersions.constFind( name );
257 if ( v != mApiVersions.constEnd() )
258 {
259 key = version.isEmpty() ? v->second : makeServiceKey( name, version );
260 const ApiTable::const_iterator it = mApis.constFind( key );
261 if ( it != mApis.constEnd() )
262 {
263 api = it->get();
264 }
265 else
266 {
267 // Return the default version
268 QgsMessageLog::logMessage( QString( "API %1 %2 not found, returning default" ).arg( name, version ),
269 QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
270 api = mApis[v->second].get();
271 }
272 }
273 else
274 {
275 QgsMessageLog::logMessage( QString( "API %1 is not registered" ).arg( name ),
276 QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
277 }
278 return api;
279}
280
281int QgsServiceRegistry::unregisterService( const QString &name, const QString &version )
282{
283 // Check that we have a service of that name
284 int removed = 0;
285 const VersionTable::const_iterator v = mServiceVersions.constFind( name );
286 if ( v != mServiceVersions.constEnd() )
287 {
288 if ( version.isEmpty() )
289 {
290 // No version specified, remove all versions
291 ServiceTable::iterator it = mServices.begin();
292 while ( it != mServices.end() )
293 {
294 if ( ( *it )->name() == name )
295 {
296 QgsMessageLog::logMessage( QString( "Unregistering service %1 %2" ).arg( name, ( *it )->version() ),
297 QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
298 it = mServices.erase( it );
299 ++removed;
300 }
301 else
302 {
303 ++it;
304 }
305 }
306 // Remove from version table
307 mServiceVersions.remove( name );
308 }
309 else
310 {
311 const QString key = makeServiceKey( name, version );
312 const ServiceTable::iterator found = mServices.find( key );
313 if ( found != mServices.end() )
314 {
315 QgsMessageLog::logMessage( QString( "Unregistering service %1 %2" ).arg( name, version ),
316 QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
317 mServices.erase( found );
318 removed = 1;
319
320 // Find if we have other services of that name
321 // but with different version
322 //
323 QString maxVer;
324 const std::function < void ( const ServiceTable::mapped_type & ) >
325 findGreaterVersion = [name, &maxVer]( const ServiceTable::mapped_type & service )
326 {
327 if ( service->name() == name &&
328 ( maxVer.isEmpty() || isVersionGreater( service->version(), maxVer ) ) )
329 maxVer = service->version();
330 };
331
332 mServiceVersions.remove( name );
333
334 std::for_each( mServices.constBegin(), mServices.constEnd(), findGreaterVersion );
335 if ( !maxVer.isEmpty() )
336 {
337 // Set the new default service
338 const QString key = makeServiceKey( name, maxVer );
339 mServiceVersions.insert( name, VersionTable::mapped_type( version, key ) );
340 }
341 }
342 }
343 }
344 return removed;
345}
346
347void QgsServiceRegistry::init( const QString &nativeModulePath, QgsServerInterface *serverIface )
348{
349 mNativeLoader.loadModules( nativeModulePath, *this, serverIface );
350}
351
353{
354 // Release all services
355 mServiceVersions.clear();
356 mServices.clear();
357 mApis.clear();
358 mNativeLoader.unloadModules();
359}
360
362{
363
364 const QString name = api->name();
365 const QString version = api->version();
366
367 // Test if service is already registered
368 const QString key = makeServiceKey( name, version );
369 if ( mApis.constFind( key ) != mApis.constEnd() )
370 {
371 QgsMessageLog::logMessage( QStringLiteral( "Error API %1 %2 is already registered" ).arg( name, version ),
372 QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
373 return false;
374 }
375
376 QgsMessageLog::logMessage( QStringLiteral( "Adding API %1 %2" ).arg( name, version ), QString(), Qgis::MessageLevel::Info );
377 mApis.insert( key, std::shared_ptr<QgsServerApi>( api ) );
378
379 // Check the default version
380 // The first inserted service of a given name
381 // is the default one.
382 // this will ensure that native services are always
383 // the defaults.
384 const VersionTable::const_iterator v = mApiVersions.constFind( name );
385 if ( v == mApiVersions.constEnd() )
386 {
387 // Insert the service as the default one
388 mApiVersions.insert( name, VersionTable::mapped_type( version, key ) );
389 }
390 return true;
391}
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).
Server generic API endpoint abstract base class.
Definition: qgsserverapi.h:81
virtual const QString version() const
Returns the version of the service.
Definition: qgsserverapi.h:106
virtual const QString name() const =0
Returns the API name.
QgsServerInterface Class defining interfaces exposed by QGIS Server and made available to plugins.
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
void unloadModules()
Unload all modules.
void loadModules(const QString &modulePath, QgsServiceRegistry &registrar, QgsServerInterface *serverIface=nullptr)
Load all modules from path.
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.
~QgsServiceRegistry()
Destructor.
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.
QgsService Class defining interfaces for QGIS server services.
Definition: qgsservice.h:39
virtual QString name() const =0
virtual QString version() const =0