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