QGIS API Documentation 3.41.0-Master (af5edcb665c)
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 ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
108 service = mServices[v->second].get();
109 }
110 }
111 else
112 {
113 QgsMessageLog::logMessage( QString( "Service %1 is not registered" ).arg( name ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
114 }
115 return service;
116}
117
119{
120 const QString name = service->name();
121 const QString version = service->version();
122
123 // Test if service is already registered
124 const QString key = makeServiceKey( name, version );
125 if ( mServices.constFind( key ) != mServices.constEnd() )
126 {
127 QgsMessageLog::logMessage( QStringLiteral( "Error Service %1 %2 is already registered" ).arg( name, version ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
128 return;
129 }
130
131 QgsMessageLog::logMessage( QStringLiteral( "Adding service %1 %2" ).arg( name, version ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
132 mServices.insert( key, std::shared_ptr<QgsService>( service ) );
133
134 // Check the default version
135 // The first inserted service of a given name
136 // is the default one.
137 // this will ensure that native services are always
138 // the defaults.
139 const VersionTable::const_iterator v = mServiceVersions.constFind( name );
140 if ( v == mServiceVersions.constEnd() )
141 {
142 // Insert the service as the default one
143 mServiceVersions.insert( name, VersionTable::mapped_type( version, key ) );
144 }
145 /*
146 if ( v != mVersions.constEnd() )
147 {
148 if ( isVersionGreater( version, v->first ) )
149 {
150 // Replace the default version key
151 mVersions.insert( name, VersionTable::mapped_type( version, key ) );
152 }
153 }
154 else
155 {
156 // Insert the service as the default one
157 mVersions.insert( name, VersionTable::mapped_type( version, key ) );
158 }*/
159}
160
161int QgsServiceRegistry::unregisterApi( const QString &name, const QString &version )
162{
163 // Check that we have an API of that name
164 int removed = 0;
165 const VersionTable::const_iterator v = mApiVersions.constFind( name );
166 if ( v != mApiVersions.constEnd() )
167 {
168 if ( version.isEmpty() )
169 {
170 // No version specified, remove all versions
171 ApiTable::iterator it = mApis.begin();
172 while ( it != mApis.end() )
173 {
174 if ( ( *it )->name() == name )
175 {
176 QgsMessageLog::logMessage( QString( "Unregistering API %1 %2" ).arg( name, ( *it )->version() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
177 it = mApis.erase( it );
178 ++removed;
179 }
180 else
181 {
182 ++it;
183 }
184 }
185 // Remove from version table
186 mApiVersions.remove( name );
187 }
188 else
189 {
190 const QString key = makeServiceKey( name, version );
191 const ApiTable::iterator found = mApis.find( key );
192 if ( found != mApis.end() )
193 {
194 QgsMessageLog::logMessage( QString( "Unregistering API %1 %2" ).arg( name, version ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
195 mApis.erase( found );
196 removed = 1;
197
198 // Find if we have other services of that name
199 // but with different version
200 //
201 QString maxVer;
202 const std::function<void( const ApiTable::mapped_type & )>
203 findGreaterVersion = [name, &maxVer]( const ApiTable::mapped_type &api ) {
204 if ( api->name() == name && ( maxVer.isEmpty() || isVersionGreater( api->version(), maxVer ) ) )
205 maxVer = api->version();
206 };
207
208 mApiVersions.remove( name );
209
210 std::for_each( mApis.constBegin(), mApis.constEnd(), findGreaterVersion );
211 if ( !maxVer.isEmpty() )
212 {
213 // Set the new default service
214 const QString key = makeServiceKey( name, maxVer );
215 mApiVersions.insert( name, VersionTable::mapped_type( version, key ) );
216 }
217 }
218 }
219 }
220 return removed;
221}
222
224{
225 for ( const auto &api : mApis )
226 {
227 QgsMessageLog::logMessage( QStringLiteral( "Trying URL path: '%1' for '%2'" ).arg( request.url().path(), api->rootPath() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
228 if ( api->accept( request.url() ) )
229 {
230 Q_ASSERT( !api->name().isEmpty() );
231 QgsMessageLog::logMessage( QStringLiteral( "API %1 accepts the URL path '%2' " ).arg( api->name(), request.url().path() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
232 return api.get();
233 }
234 }
235 return nullptr;
236}
237
238QgsServerApi *QgsServiceRegistry::getApi( const QString &name, const QString &version )
239{
240 QgsServerApi *api = nullptr;
241 QString key;
242
243 // Check that we have an API with that name
244 const VersionTable::const_iterator v = mApiVersions.constFind( name );
245 if ( v != mApiVersions.constEnd() )
246 {
247 key = version.isEmpty() ? v->second : makeServiceKey( name, version );
248 const ApiTable::const_iterator it = mApis.constFind( key );
249 if ( it != mApis.constEnd() )
250 {
251 api = it->get();
252 }
253 else
254 {
255 // Return the default version
256 QgsMessageLog::logMessage( QString( "API %1 %2 not found, returning default" ).arg( name, version ), QStringLiteral( "Server" ), Qgis::MessageLevel::Warning );
257 api = mApis[v->second].get();
258 }
259 }
260 else
261 {
262 QgsMessageLog::logMessage( QString( "API %1 is not registered" ).arg( name ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
263 }
264 return api;
265}
266
267int QgsServiceRegistry::unregisterService( const QString &name, const QString &version )
268{
269 // Check that we have a service of that name
270 int removed = 0;
271 const VersionTable::const_iterator v = mServiceVersions.constFind( name );
272 if ( v != mServiceVersions.constEnd() )
273 {
274 if ( version.isEmpty() )
275 {
276 // No version specified, remove all versions
277 ServiceTable::iterator it = mServices.begin();
278 while ( it != mServices.end() )
279 {
280 if ( ( *it )->name() == name )
281 {
282 QgsMessageLog::logMessage( QString( "Unregistering service %1 %2" ).arg( name, ( *it )->version() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
283 it = mServices.erase( it );
284 ++removed;
285 }
286 else
287 {
288 ++it;
289 }
290 }
291 // Remove from version table
292 mServiceVersions.remove( name );
293 }
294 else
295 {
296 const QString key = makeServiceKey( name, version );
297 const ServiceTable::iterator found = mServices.find( key );
298 if ( found != mServices.end() )
299 {
300 QgsMessageLog::logMessage( QString( "Unregistering service %1 %2" ).arg( name, version ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
301 mServices.erase( found );
302 removed = 1;
303
304 // Find if we have other services of that name
305 // but with different version
306 //
307 QString maxVer;
308 const std::function<void( const ServiceTable::mapped_type & )>
309 findGreaterVersion = [name, &maxVer]( const ServiceTable::mapped_type &service ) {
310 if ( service->name() == name && ( maxVer.isEmpty() || isVersionGreater( service->version(), maxVer ) ) )
311 maxVer = service->version();
312 };
313
314 mServiceVersions.remove( name );
315
316 std::for_each( mServices.constBegin(), mServices.constEnd(), findGreaterVersion );
317 if ( !maxVer.isEmpty() )
318 {
319 // Set the new default service
320 const QString key = makeServiceKey( name, maxVer );
321 mServiceVersions.insert( name, VersionTable::mapped_type( version, key ) );
322 }
323 }
324 }
325 }
326 return removed;
327}
328
329void QgsServiceRegistry::init( const QString &nativeModulePath, QgsServerInterface *serverIface )
330{
331 mNativeLoader.loadModules( nativeModulePath, *this, serverIface );
332}
333
335{
336 // Release all services
337 mServiceVersions.clear();
338 mServices.clear();
339 mApis.clear();
340 mNativeLoader.unloadModules();
341}
342
344{
345 const QString name = api->name();
346 const QString version = api->version();
347
348 // Test if service is already registered
349 const QString key = makeServiceKey( name, version );
350 if ( mApis.constFind( key ) != mApis.constEnd() )
351 {
352 QgsMessageLog::logMessage( QStringLiteral( "Error API %1 %2 is already registered" ).arg( name, version ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
353 return false;
354 }
355
356 QgsMessageLog::logMessage( QStringLiteral( "Adding API %1 %2" ).arg( name, version ), QString(), Qgis::MessageLevel::Info );
357 mApis.insert( key, std::shared_ptr<QgsServerApi>( api ) );
358
359 // Check the default version
360 // The first inserted service of a given name
361 // is the default one.
362 // this will ensure that native services are always
363 // the defaults.
364 const VersionTable::const_iterator v = mApiVersions.constFind( name );
365 if ( v == mApiVersions.constEnd() )
366 {
367 // Insert the service as the default one
368 mApiVersions.insert( name, VersionTable::mapped_type( version, key ) );
369 }
370 return true;
371}
@ Warning
Warning message.
Definition qgis.h:156
@ Critical
Critical/error message.
Definition qgis.h:157
@ Info
Information message.
Definition qgis.h:155
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.
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