QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 
29 namespace
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 
89 QgsService *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 
166 int 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 
250 QgsServerApi *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 
281 int 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 
347 void 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 }
QgsServiceRegistry::getService
QgsService * getService(const QString &name, const QString &version=QString())
Retrieve a service from its name.
Definition: qgsserviceregistry.cpp:89
QgsServerApi::version
virtual const QString version() const
Returns the version of the service.
Definition: qgsserverapi.h:106
QgsService
QgsService Class defining interfaces for QGIS server services.
Definition: qgsservice.h:39
QgsServiceRegistry::registerService
void registerService(QgsService *service)
Register a service by its name and version.
Definition: qgsserviceregistry.cpp:120
qgsservice.h
QgsServerRequest
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
Definition: qgsserverrequest.h:38
qgsserviceregistry.h
QgsServiceRegistry::unregisterService
int unregisterService(const QString &name, const QString &version=QString())
Unregister service from its name and version.
Definition: qgsserviceregistry.cpp:281
QgsServiceRegistry::registerApi
bool registerApi(QgsServerApi *api)
Registers the QgsServerApi api.
Definition: qgsserviceregistry.cpp:361
QgsServiceRegistry::unregisterApi
int unregisterApi(const QString &name, const QString &version=QString())
Unregisters API from its name and version.
Definition: qgsserviceregistry.cpp:166
QgsServiceRegistry::apiForRequest
QgsServerApi * apiForRequest(const QgsServerRequest &request) const
Searches the API register for an API matching the request and returns a (possibly NULL) pointer to it...
Definition: qgsserviceregistry.cpp:233
QgsService::name
virtual QString name() const =0
QgsServiceNativeLoader::unloadModules
void unloadModules()
Unload all modules.
Definition: qgsservicenativeloader.cpp:133
QgsServiceRegistry::init
void init(const QString &nativeModulepath, QgsServerInterface *serverIface=nullptr)
Initialize registry, load modules and auto register services.
Definition: qgsserviceregistry.cpp:347
QgsServiceRegistry::~QgsServiceRegistry
~QgsServiceRegistry()
Destructor.
Definition: qgsserviceregistry.cpp:84
qgsserverapi.h
QgsServerApi::name
virtual const QString name() const =0
Returns the API name.
QgsMessageLog::logMessage
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).
Definition: qgsmessagelog.cpp:27
QgsServiceRegistry::cleanUp
void cleanUp()
Clean up registered service and unregister modules.
Definition: qgsserviceregistry.cpp:352
QgsServiceNativeLoader::loadModules
void loadModules(const QString &modulePath, QgsServiceRegistry &registrar, QgsServerInterface *serverIface=nullptr)
Load all modules from path.
Definition: qgsservicenativeloader.cpp:55
QgsServerRequest::url
QUrl url() const
Definition: qgsserverrequest.cpp:86
QgsServerApi
Server generic API endpoint abstract base class.
Definition: qgsserverapi.h:80
QgsServiceRegistry::getApi
QgsServerApi * getApi(const QString &name, const QString &version=QString())
Retrieves an API from its name.
Definition: qgsserviceregistry.cpp:250
QgsServerInterface
QgsServerInterface Class defining interfaces exposed by QGIS Server and made available to plugins.
Definition: qgsserverinterface.h:60
QgsService::version
virtual QString version() const =0
qgsmessagelog.h