QGIS API Documentation 3.39.0-Master (47f7b3a4989)
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#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
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}
@ Warning
Warning message.
Definition qgis.h:101
@ Critical
Critical/error message.
Definition qgis.h:102
@ Info
Information message.
Definition qgis.h:100
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.
virtual const QString version() const
Returns the version of the service.
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