1/****************************************************************************
2**
3** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtSystems module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL21$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 or version 3 as published by the Free
20** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22** following information to ensure the GNU Lesser General Public License
23** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** As a special exception, The Qt Company gives you certain additional
27** rights. These rights are described in The Qt Company LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** $QT_END_LICENSE$
31**
32****************************************************************************/
33
34#include "qservicemanager.h"
35#include "qserviceplugininterface.h"
36#include "qserviceinterfacedescriptor_p.h"
37#include "qremoteserviceregister_p.h"
38#include "qremoteserviceregisterentry_p.h"
39#include "qserviceoperations_p.h"
40#include "qservicereply.h"
41#include "qservicerequest_p.h"
42#include "qservicedebuglog_p.h"
43
44#include "databasemanager_p.h"
45
46#include <QObject>
47#include <QMetaMethod>
48#include <QPluginLoader>
49#include <QFile>
50#include <QCoreApplication>
51#include <QDir>
52#include <QSystemSemaphore>
53#include <QScopedPointer>
54
55QT_BEGIN_NAMESPACE
56
57QString QServiceManager::resolveLibraryPath(const QString &libNameOrPath)
58{
59 if (QFile::exists(fileName: libNameOrPath))
60 return libNameOrPath;
61
62 // try to find plug-in via QLibrary
63 QStringList paths = QCoreApplication::libraryPaths();
64#ifdef QTM_PLUGIN_PATH
65 paths << QLatin1String(QTM_PLUGIN_PATH)+QLatin1String("/serviceframework");
66#endif
67 for (int i=0; i<paths.count(); i++) {
68 QString libPath = QDir::toNativeSeparators(pathName: paths[i]) + QDir::separator() + libNameOrPath;
69
70 QLibrary lib(libPath);
71 if (lib.load()) {
72 lib.unload();
73 return lib.fileName();
74 }
75 }
76 return QString();
77}
78
79class QServicePluginCleanup : public QObject
80{
81 Q_OBJECT
82public:
83 QServicePluginCleanup(QPluginLoader *loader, QObject *parent = 0)
84 : QObject(parent),
85 m_loader(loader)
86 {
87 }
88
89 ~QServicePluginCleanup()
90 {
91 if (m_loader) {
92 //m_loader->unload();
93 delete m_loader;
94 }
95 }
96
97 QPluginLoader *m_loader;
98};
99
100class QServiceManagerPrivate : public QObject
101{
102 Q_OBJECT
103public:
104 QServiceManager *manager;
105 DatabaseManager *dbManager;
106 QServiceOperations *ops;
107 QService::Scope scope;
108 QServiceManager::Error error;
109
110 QServiceManagerPrivate(QServiceManager *parent = 0)
111 : QObject(parent),
112 manager(parent),
113 dbManager(new DatabaseManager),
114 ops(0)
115 {
116 connect(asender: dbManager, SIGNAL(serviceAdded(QString, DatabaseManager::DbScope)),
117 SLOT(serviceAdded(QString, DatabaseManager::DbScope)));
118 connect(asender: dbManager, SIGNAL(serviceRemoved(QString, DatabaseManager::DbScope)),
119 SLOT(serviceRemoved(QString, DatabaseManager::DbScope)));
120 }
121
122 ~QServiceManagerPrivate()
123 {
124 delete dbManager;
125 }
126
127 void setError(QServiceManager::Error err)
128 {
129 if (error != err)
130 {
131 error = err;
132 manager->errorChanged();
133 }
134 }
135
136 void setError()
137 {
138 QServiceManager::Error prev = error;
139 switch (dbManager->lastError().code()) {
140 case DBError::NoError:
141 error = QServiceManager::NoError;
142 break;
143 case DBError::DatabaseNotOpen:
144 case DBError::InvalidDatabaseConnection:
145 case DBError::CannotCreateDbDir:
146 case DBError::CannotOpenServiceDb:
147 case DBError::NoWritePermissions:
148 case DBError::InvalidDatabaseFile:
149 error = QServiceManager::StorageAccessError;
150 break;
151 case DBError::LocationAlreadyRegistered:
152 error = QServiceManager::ServiceAlreadyExists;
153 break;
154 case DBError::IfaceImplAlreadyRegistered:
155 error = QServiceManager::ImplementationAlreadyExists;
156 break;
157 case DBError::NotFound:
158 error = QServiceManager::ComponentNotFound;
159 break;
160 case DBError::InvalidDescriptorScope:
161 error = QServiceManager::InvalidServiceInterfaceDescriptor;
162 break;
163 case DBError::SqlError:
164 case DBError::IfaceIDNotExternal:
165 case DBError::ExternalIfaceIDFound:
166 case DBError::UnknownError:
167 error = QServiceManager::UnknownError;
168 break;
169 }
170 if (prev != error)
171 manager->errorChanged();
172 }
173
174private Q_SLOTS:
175 void serviceAdded(const QString &service, DatabaseManager::DbScope dbScope)
176 {
177 QService::Scope s = (dbScope == DatabaseManager::SystemScope ?
178 QService::SystemScope : QService::UserScope);
179 emit manager->serviceAdded(serviceName: service, scope: s);
180 }
181
182 void serviceRemoved(const QString &service, DatabaseManager::DbScope dbScope)
183 {
184 QService::Scope s = (dbScope == DatabaseManager::SystemScope ?
185 QService::SystemScope : QService::UserScope);
186 emit manager->serviceRemoved(serviceName: service, scope: s);
187 }
188};
189
190/*!
191 \class QServiceManager
192 \ingroup servicefw
193 \inmodule QtServiceFramework
194 \brief The QServiceManager class enables the loading of service plugins
195 and the (de)registration of services.
196
197 A service is a stand-alone component that can be used by multiple clients.
198 Each service implementation must derive from QObject. Clients request a
199 reference to a service via \l loadInterface() or \l loadLocalTypedInterface().
200
201 Services are separate deliveries in the form of plug-ins. New services can be (de)registered
202 at any time via \l addService() and \l removeService() respectively. Such an event is
203 published via the \l serviceAdded() and \l serviceRemoved() signal.
204 Each service plug-in must implement QServicePluginInterface.
205
206 Each plug-in may support multiple interfaces and may even provide multiple implementations
207 for the same interface. Individual implementations are identified via
208 QServiceInterfaceDescriptor. For a more detailed explanation of services and how they relate to
209 interface and their implementations please see QServiceInterfaceDescriptor.
210
211 \sa QServicePluginInterface
212*/
213
214/*!
215 \enum QServiceManager::Error
216 Defines the possible errors for the service manager.
217
218 \value NoError No error occurred.
219 \value StorageAccessError The service data storage is not accessible. This could be because the caller does not have the required permissions.
220 \value InvalidServiceLocation The service was not found at its specified \l{QServiceInterfaceDescriptor::Location}{location}.
221 \value InvalidServiceXml The XML defining the service metadata is invalid.
222 \value InvalidServiceInterfaceDescriptor The service interface descriptor is invalid, or refers to an interface implementation that cannot be accessed in the current scope.
223 \value ServiceAlreadyExists Another service has previously been registered with the same \l{QServiceInterfaceDescriptor::Location}{location}.
224 \value ImplementationAlreadyExists Another service that implements the same interface version has previously been registered.
225 \value PluginLoadingFailed The service plugin cannot be loaded.
226 \value ComponentNotFound The service or interface implementation has not been registered.
227 \value ServiceCapabilityDenied The security session does not permit service access based on its capabilities.
228 \value UnknownError An unknown error occurred.
229*/
230
231/*!
232 \fn void QServiceManager::serviceAdded(const QString& serviceName, QService::Scope scope)
233
234 This signal is emited whenever a new service with the given
235 \a serviceName has been registered with the service manager.
236 \a scope indicates where the service was added.
237
238 If the manager scope is QService::SystemScope, it will not receive
239 notifications about services added in the user scope.
240
241 \sa addService()
242*/
243
244/*!
245 \fn void QServiceManager::serviceRemoved(const QString& serviceName, QService::Scope scope)
246
247 This signal is emited whenever a service with the given
248 \a serviceName has been deregistered with the service manager.
249 \a scope indicates where the service was added.
250
251 If the manager scope is QService::SystemScope, it will not receive
252 notifications about services removed in the user scope.
253
254 \sa removeService()
255*/
256
257/*!
258 Creates a service manager with the given \a parent.
259
260 The scope will default to QService::UserScope.
261
262 The service manager will also ensure that a background thread is started to handle
263 service manager requests. If you need to supress this behavior so that all requests
264 are handled in the foreground (in the main GUI thread) export the following environment
265 variable:
266 \code
267 env QT_NO_SFW_BACKGROUND_OPERATION /path/to/my_sfw_app
268 \endcode
269*/
270QServiceManager::QServiceManager(QObject *parent)
271 : QObject(parent),
272 d(new QServiceManagerPrivate(this))
273{
274 qRegisterMetaType<QService::UnrecoverableIPCError>(typeName: "QService::UnrecoverableIPCError");
275 d->scope = QService::UserScope;
276}
277
278/*!
279 Creates a service manager with the given \a scope and \a parent.
280*/
281QServiceManager::QServiceManager(QService::Scope scope, QObject *parent)
282 : QObject(parent),
283 d(new QServiceManagerPrivate(this))
284{
285 d->scope = scope;
286}
287
288/*!
289 Destroys the service manager.
290*/
291QServiceManager::~QServiceManager()
292{
293 if (d->ops)
294 d->ops->disengage();
295 delete d;
296}
297
298/*!
299 Returns the scope used for registering and searching of services.
300*/
301QService::Scope QServiceManager::scope() const
302{
303 return d->scope;
304}
305
306/*!
307 Returns a list of the services that provide the interface specified by
308 \a interfaceName. If \a interfaceName is empty, this function returns
309 a list of all available services in this manager's scope.
310*/
311QStringList QServiceManager::findServices(const QString& interfaceName) const
312{
313 d->setError(QServiceManager::NoError);
314 QStringList services;
315 services = d->dbManager->getServiceNames(interfaceName,
316 scope: d->scope == QService::SystemScope ? DatabaseManager::SystemScope : DatabaseManager::UserScope);
317 d->setError();
318 return services;
319}
320
321/*!
322 Returns a list of the interfaces that match the specified \a filter.
323*/
324QList<QServiceInterfaceDescriptor> QServiceManager::findInterfaces(const QServiceFilter& filter) const
325{
326 d->setError(QServiceManager::NoError);
327 QList<QServiceInterfaceDescriptor> descriptors = d->dbManager->getInterfaces(filter,
328 scope: d->scope == QService::SystemScope ? DatabaseManager::SystemScope : DatabaseManager::UserScope);
329 if (descriptors.isEmpty() && d->dbManager->lastError().code() != DBError::NoError) {
330 d->setError();
331 return QList<QServiceInterfaceDescriptor>();
332 }
333 return descriptors;
334}
335
336/*!
337 Returns a list of the interfaces provided by the service named
338 \a serviceName. If \a serviceName is empty, this function returns
339 a list of all available interfaces in this manager's scope.
340*/
341QList<QServiceInterfaceDescriptor> QServiceManager::findInterfaces(const QString& serviceName) const
342{
343 QServiceFilter filter;
344 if (!serviceName.isEmpty())
345 filter.setServiceName(serviceName);
346 return findInterfaces(filter);
347}
348
349/*!
350 Loads and returns the interface specified by \a interfaceName.
351
352 The caller takes ownership of the returned pointer.
353
354 This function returns a null pointer if the requested service cannot be found.
355
356 \sa setInterfaceDefault(), interfaceDefault()
357*/
358QObject* QServiceManager::loadInterface(const QString& interfaceName)
359{
360 return loadInterface(descriptor: interfaceDefault(interfaceName));
361}
362
363/*!
364 \internal
365 Private helper function to get the QObject for an InterProcess service.
366*/
367QObject *QServiceManager::loadInterProcessService(const QServiceInterfaceDescriptor &descriptor,
368 const QString &serviceLocation) const
369{
370 //ipc service
371 const int majorversion = descriptor.majorVersion();
372 const int minorversion = descriptor.minorVersion();
373 QString version = QString::number(majorversion) + QLatin1String(".") + QString::number(minorversion);
374
375 QRemoteServiceRegister::Entry serviceEntry;
376 serviceEntry.d->iface = descriptor.interfaceName();
377 serviceEntry.d->service = descriptor.serviceName();
378 serviceEntry.d->ifaceVersion = version;
379
380 QObject* service = QRemoteServiceRegisterPrivate::proxyForService(entry: serviceEntry, location: serviceLocation);
381 if (!service)
382 d->setError(QServiceManager::InvalidServiceLocation);
383
384 //client owns proxy object
385 return service;
386}
387
388/*!
389 \internal
390 Private helper function to get the QObject for an InProcess (plugin-based) service.
391*/
392QObject *QServiceManager::loadInProcessService(const QServiceInterfaceDescriptor& descriptor,
393 const QString &serviceFilePath) const
394{
395 QScopedPointer<QPluginLoader> loader(new QPluginLoader(serviceFilePath));
396 QObject *obj = 0;
397
398 // pluginIFace is same for all service instances of the same plugin
399 // calling loader->unload deletes pluginIFace automatically if no other
400 // service instance is around
401 QServicePluginInterface *pluginIFace = qobject_cast<QServicePluginInterface *>(object: loader->instance());
402 if (pluginIFace) {
403 //check initialization first as the service may be a pre-registered one
404 bool doLoading = true;
405 QString serviceInitialized = descriptor.customAttribute(which: QLatin1String(SERVICE_INITIALIZED_ATTR));
406 if (!serviceInitialized.isEmpty() && (serviceInitialized == QLatin1String("NO"))) {
407 // open/create the semaphore using the service's name as identifier
408 QSystemSemaphore semaphore(descriptor.serviceName(), 1);
409 if (semaphore.error() != QSystemSemaphore::NoError) {
410 //try to create it
411 semaphore.setKey(key: descriptor.serviceName(), initialValue: 1, mode: QSystemSemaphore::Create);
412 }
413 if (semaphore.error() == QSystemSemaphore::NoError && semaphore.acquire()) {
414 pluginIFace->installService();
415 DatabaseManager::DbScope scope = d->scope == QService::UserScope ?
416 DatabaseManager::UserOnlyScope : DatabaseManager::SystemScope;
417 d->dbManager->serviceInitialized(serviceName: descriptor.serviceName(), scope);
418 semaphore.release();
419 } else {
420 qWarning() << semaphore.errorString();
421 doLoading = false;
422
423 }
424 }
425 if (doLoading) {
426 obj = pluginIFace->createInstance(descriptor);
427 if (obj) {
428 QServicePluginCleanup *cleanup = new QServicePluginCleanup(loader.take());
429 QObject::connect(sender: obj, SIGNAL(destroyed()), receiver: cleanup, SLOT(deleteLater()));
430 } else {
431 qWarning() << "Cannot create object instance for "
432 << descriptor.interfaceName() << ":"
433 << serviceFilePath;
434 }
435 }
436 } else {
437 qWarning() << "QServiceManager::loadInterface():" << serviceFilePath << loader->errorString();
438 }
439 return obj;
440}
441
442/*!
443 Loads and returns the interface specified by \a descriptor.
444
445 The caller takes ownership of the returned pointer.
446
447 This function returns a null pointer if the requested service cannot be found.
448
449 \sa loadInterfaceRequest()
450*/
451QObject* QServiceManager::loadInterface(const QServiceInterfaceDescriptor& descriptor)
452{
453 qServiceLog() << "class" << "QServiceManager"
454 << "event" << "loadInterface"
455 << "interface" << descriptor.interfaceName()
456 << "service" << descriptor.serviceName();
457
458 d->setError(QServiceManager::NoError);
459 if (!descriptor.isValid()) {
460 d->setError(QServiceManager::InvalidServiceInterfaceDescriptor);
461 return 0;
462 }
463
464 QObject *obj = 0;
465 int serviceType = descriptor.attribute(which: QServiceInterfaceDescriptor::ServiceType).toInt();
466 QString location = descriptor.attribute(which: QServiceInterfaceDescriptor::Location).toString();
467
468 if (serviceType == QService::InterProcess)
469 {
470 return loadInterProcessService(descriptor, serviceLocation: location);
471 }
472 else
473 {
474 QString serviceFilePath = resolveLibraryPath(libNameOrPath: location);
475 if (serviceFilePath.isEmpty())
476 {
477 d->setError(QServiceManager::InvalidServiceLocation);
478 return 0;
479 }
480
481 obj = loadInProcessService(descriptor, serviceFilePath);
482 if (!obj)
483 d->setError(QServiceManager::PluginLoadingFailed);
484 }
485 return obj;
486}
487
488/*!
489 \fn QServiceReply *loadInterfaceRequest(const QString &interfaceName)
490 Initiate a background request to load the interface specified by \a interfaceName, and
491 return a QServiceReply object to track the results of the request.
492*/
493
494/*!
495 \fn QServiceReply *loadInterfaceRequest(const QServiceInterfaceDescriptor& descriptor)
496 Initiate a background request to load the interface specified by \a descriptor, and
497 return a QServiceReply object to track the results of the request.
498*/
499
500
501/*!
502 \fn QServiceReplyTyped<T> *loadLocalTypedInterfaceRequest(const QString& interfaceName)
503 Initiate a background request to load the interface specified by \a interfaceName, and
504 return a QServiceReplyTyped object to track the results of the request.
505 \code
506 // a member variable to track the reply
507 QServiceReply<SomeKnownService> *m_reply;
508
509 // make the request
510 m_reply = *mgr->loadLocalTypedInterfaceRequest<SomeKnownService>(sksIfaceName);
511 connect(m_reply, SIGNAL(finished()), this, SLOT(handleServiceInfo));
512
513 // ...later, in the handler
514 SomeKnownService *svc = m_reply->proxyObject();
515 \endcode
516*/
517
518/*!
519 \fn QServiceReplyTyped<T> *loadLocalTypedInterfaceRequest(const QServiceInterfaceDescriptor& descriptor)
520 Initiate a background request to load the interface specified by \a interfaceName, and
521 return a QServiceReplyTyped object to track the results of the request.
522*/
523
524/*!
525 \internal
526 Initiate a background request to load and return the interface specified by \a interfaceName,
527 and using the given \a reply object, which (if non-null) will be a typed sub-class object.
528 If it is null a default QServiceReply is constructed.
529*/
530QServiceReply *QServiceManager::loadInterfaceRequest(const QString &interfaceName)
531{
532 QServiceReply *reply = new QServiceReply;
533
534 if (!qgetenv(varName: "QT_NO_SFW_BACKGROUND_OPERATION").isEmpty())
535 {
536 qWarning(msg: "Turning off sfw background operations as requested.");
537 return 0;
538 }
539
540 if (!d->ops) {
541 d->ops = QServiceOperations::instance();
542 d->ops->engage();
543 }
544
545 reply->setRequest(interfaceName);
546
547 QServiceRequest req(interfaceName);
548 req.setReply(reply);
549 req.setScope(scope());
550 d->ops->initiateRequest(req);
551
552 return reply;
553}
554
555/*!
556 \internal
557 Initiate a background request to load and return the interface specified by \a descriptor,
558 and using the given \a reply object, which (if non-null) will be a typed sub-class object.
559 If it is null a default QServiceReply is constructed.
560*/
561QServiceReply *QServiceManager::loadInterfaceRequest(const QServiceInterfaceDescriptor &descriptor)
562{
563 QServiceReply *reply = new QServiceReply();
564
565 if (!d->ops) {
566 d->ops = QServiceOperations::instance();
567 d->ops->engage();
568 }
569
570 reply->setRequest(descriptor.interfaceName());
571
572 QServiceRequest req(descriptor);
573 req.setReply(reply);
574 req.setScope(scope());
575 d->ops->initiateRequest(req);
576
577 return reply;
578}
579
580
581/*!
582 If \a interfaceName is an out of process service this verifies the
583 interface is running. If the service is in process this function
584 always returns false.
585
586 Use this function to verify the interface requested is running. This
587 is useful is you only want to call loadInterface when the service
588 is already running. This call does not guarantee that the service
589 will remain running, as such a race condition exists if the service
590 quits between this call being made and loadInterface being called.
591
592 If the service can not be fount this returns false.
593
594 \sa setInterfaceDefault(), interfaceDefault(), loadInterface()
595*/
596bool QServiceManager::isInterfaceRunning(const QString& interfaceName)
597{
598 return isInterfaceRunning(descriptor: interfaceDefault(interfaceName));
599}
600
601/*!
602 If \a descriptor is an out of process service this verifies the
603 service is running. If the service is in process this function
604 always returns false.
605
606 Use this function to verify the interface requested is running. This
607 is useful is you only want to call loadInterface when the service
608 is already running. This call does not guarantee that the service
609 will remain running, as such a race condition exists if the service
610 quits between this call being made and loadInterface being called.
611
612 If the service can not be found this returns false. Error is set
613 if an error occurs.
614
615*/
616bool QServiceManager::isInterfaceRunning(const QServiceInterfaceDescriptor& descriptor)
617{
618 d->setError(NoError);
619 if (!descriptor.isValid()) {
620 d->setError(InvalidServiceInterfaceDescriptor);
621 return false;
622 }
623
624 const QString location = descriptor.attribute(which: QServiceInterfaceDescriptor::Location).toString();
625 const bool isInterProcess = (descriptor.attribute(which: QServiceInterfaceDescriptor::ServiceType).toInt()
626 == QService::InterProcess);
627 if (isInterProcess) {
628 //ipc service
629 const int majorversion = descriptor.majorVersion();
630 const int minorversion = descriptor.minorVersion();
631 QString version = QString::number(majorversion) + QLatin1String(".") + QString::number(minorversion);
632
633 QRemoteServiceRegister::Entry serviceEntry;
634 serviceEntry.d->iface = descriptor.interfaceName();
635 serviceEntry.d->service = descriptor.serviceName();
636 serviceEntry.d->ifaceVersion = version;
637
638 return QRemoteServiceRegisterPrivate::isServiceRunning(serviceEntry, location);
639 }
640
641 return false;
642}
643
644
645
646/*!
647 \fn T* QServiceManager::loadLocalTypedInterface(const QString& interfaceName)
648
649 Loads the service object implementing \a interfaceName,
650 as provided by the default service for this interface.
651 The template class must be derived from QObject.
652
653 If \a interfaceName is not a known interface the returned pointer will be null.
654
655 Note that using this function implies that service and client share
656 the implementation of T which means that service and client become tightly coupled.
657 This may cause issue during later updates as certain changes may require code changes
658 to the service and client.
659
660 The caller takes ownership of the returned pointer.
661
662 \sa setInterfaceDefault(), interfaceDefault()
663*/
664
665
666/*!
667 \fn T* QServiceManager::loadLocalTypedInterface(const QServiceInterfaceDescriptor& serviceDescriptor)
668
669 Loads the service object identified by \a serviceDescriptor.
670 The template class must be derived from QObject.
671
672 If the \a serviceDescriptor is not valid the returned pointer will be null.
673
674 Note that using this function implies that service and client share
675 the implementation of T which means that service and client become tightly coupled.
676 This may cause issue during later updates as certain changes may require code changes
677 to the service and client.
678
679 The caller takes ownership of the returned pointer.
680
681*/
682
683/*!
684 Registers the service defined by the XML file at \a xmlFilePath.
685 Returns true if the registration succeeded, and false otherwise.
686
687 If a previously unkown interface is added the newly registered service automatically
688 becomes the new default service provider for the new interface.
689
690 A service plugin cannot be added if another service is already registered
691 with the same plugin file path. A service plugin also cannot be added if
692 the service is already registered and implements any of the same interface
693 versions that the new plugin implements.
694
695 \sa removeService(), setInterfaceDefault()
696*/
697bool QServiceManager::addService(const QString& xmlFilePath)
698{
699 QFile *f = new QFile(xmlFilePath);
700 bool b = addService(xmlDevice: f);
701 delete f;
702 return b;
703}
704
705/*!
706 Registers the service defined by the XML data from the given \a device.
707 Returns true if the registration succeeded, and false otherwise. If a
708 previously unkown interface is added the newly registered service
709 automatically becomes the new default service provider for the new
710 interface.
711
712 Registering a service also causes QServicePluginInterface::installService()
713 to be called on the service. If the service plugin is not accessible
714 (e.g. if the plugin file is not found) and \c installService() cannot
715 be invoked on the service, the registration fails and this method returns
716 false.
717
718 A service plugin cannot be added if another service is already registered
719 with the same plugin file path. A service plugin also cannot be added if
720 the service is already registered and implements any of the same interface
721 versions that the new plugin implements.
722
723 Services are always added based on the \l scope() of the current
724 service manager instance.
725
726 \sa removeService(), setInterfaceDefault()
727*/
728bool QServiceManager::addService(QIODevice *device)
729{
730 d->setError(QServiceManager::NoError);
731 ServiceMetaData parser(device);
732 if (!parser.extractMetadata()) {
733 d->setError(QServiceManager::InvalidServiceXml);
734 return false;
735 }
736 const ServiceMetaDataResults data = parser.parseResults();
737
738 DatabaseManager::DbScope scope = d->scope == QService::UserScope ?
739 DatabaseManager::UserOnlyScope : DatabaseManager::SystemScope;
740 ServiceMetaDataResults results = parser.parseResults();
741
742 bool result = d->dbManager->registerService(service&: results, scope);
743
744 if (results.type == QService::InterProcess)
745 return result;
746
747 //test the new plug-in
748 if (result) {
749 QPluginLoader *loader = new QPluginLoader(resolveLibraryPath(libNameOrPath: data.location));
750 QServicePluginInterface *pluginIFace = qobject_cast<QServicePluginInterface *>(object: loader->instance());
751 if (pluginIFace) {
752 pluginIFace->installService();
753 } else {
754 d->setError(QServiceManager::PluginLoadingFailed);
755 result = false;
756 qWarning() << "QServiceManager::addService()" << data.location << "->"
757 << resolveLibraryPath(libNameOrPath: data.location) << ":"
758 << loader->errorString() << " - Aborting registration";
759 d->dbManager->unregisterService(serviceName: data.name, scope);
760 }
761 //loader->unload();
762 delete loader;
763 } else {
764 d->setError();
765 }
766
767 return result;
768}
769
770/*!
771 Unregisters the service specified by \a serviceName.
772
773 Returns true if the unregistration succeeded, and false otherwise.
774
775 If a default service implementation is removed and there are other implementations
776 for the same interface, the service manager chooses the implementation with the
777 highest version number as the new default. If there is more than one serivce
778 with the same version number, the service manager makes a random choice with
779 regards to the new default implementation. If this is
780 not the desired behaviour the default selection should be updated
781 via setInterfaceDefault().
782
783 Services are always removed based on the \l scope() of the current
784 service manager instance.
785
786 \sa addService()
787*/
788bool QServiceManager::removeService(const QString& serviceName)
789{
790 d->setError(QServiceManager::NoError);
791 if (serviceName.isEmpty()) {
792 d->setError(QServiceManager::ComponentNotFound);
793 return false;
794 }
795
796 // Call QServicePluginInterface::uninstallService() on all plugins that
797 // match this service
798
799 QSet<QString> pluginPathsSet;
800 QList<QServiceInterfaceDescriptor> descriptors = findInterfaces(serviceName);
801 for (int i=0; i<descriptors.count(); i++) {
802 const QString loc = descriptors[i].attribute(which: QServiceInterfaceDescriptor::Location).toString();
803 const int type = descriptors[i].attribute(which: QServiceInterfaceDescriptor::ServiceType).toInt();
804 //exclude ipc services
805 if (type <= QService::Plugin)
806 pluginPathsSet << loc;
807 }
808
809 QList<QString> pluginPaths = pluginPathsSet.toList();
810 for (int i=0; i<pluginPaths.count(); i++) {
811 QPluginLoader *loader = new QPluginLoader(resolveLibraryPath(libNameOrPath: pluginPaths[i]));
812 QServicePluginInterface *pluginIFace = qobject_cast<QServicePluginInterface *>(object: loader->instance());
813 if (pluginIFace)
814 pluginIFace->uninstallService();
815 else
816 qWarning() << "QServiceManager: unable to invoke uninstallService() on removed service";
817 //loader->unload();
818 delete loader;
819 }
820
821 if (!d->dbManager->unregisterService(serviceName, scope: d->scope == QService::UserScope ?
822 DatabaseManager::UserOnlyScope : DatabaseManager::SystemScope)) {
823 d->setError();
824 return false;
825 }
826 return true;
827}
828
829/*!
830 Sets the default interface implementation for \a interfaceName to the
831 matching interface implementation provided by \a service.
832
833 If \a service provides more than one interface implementation for
834 \a interfaceName, the newest version of the interface is set as the
835 default.
836
837 Returns true if the operation succeeded, and false otherwise.
838
839 \b {Note:} When in system scope, the \a service must be a system-wide
840 service rather than a user-specific service; otherwise, this will fail.
841*/
842bool QServiceManager::setInterfaceDefault(const QString &service, const QString &interfaceName)
843{
844 d->setError(QServiceManager::NoError);
845 if (service.isEmpty() || interfaceName.isEmpty()) {
846 d->setError(QServiceManager::ComponentNotFound);
847 return false;
848 }
849 DatabaseManager::DbScope scope = d->scope == QService::SystemScope ?
850 DatabaseManager::SystemScope : DatabaseManager::UserScope;
851 if (!d->dbManager->setInterfaceDefault(serviceName: service, interfaceName, scope)) {
852 d->setError();
853 return false;
854 }
855 return true;
856}
857
858/*!
859 \overload
860
861 Sets the interface implementation specified by \a descriptor to be the
862 default implementation for the particular interface specified in the
863 descriptor.
864
865 Returns true if the operation succeeded, and false otherwise.
866
867 \b {Note:} When in system scope, the \a descriptor must refer to a
868 system-wide service rather than a user-specific service; otherwise, this
869 will fail.
870*/
871bool QServiceManager::setInterfaceDefault(const QServiceInterfaceDescriptor& descriptor)
872{
873 d->setError(QServiceManager::NoError);
874 DatabaseManager::DbScope scope = d->scope == QService::SystemScope ?
875 DatabaseManager::SystemScope : DatabaseManager::UserScope;
876 if (!d->dbManager->setInterfaceDefault(serviceInterface: descriptor, scope)) {
877 d->setError();
878 return false;
879 }
880 return true;
881}
882
883/*!
884 Returns the default interface implementation for the given \a interfaceName.
885*/
886QServiceInterfaceDescriptor QServiceManager::interfaceDefault(const QString& interfaceName) const
887{
888 qDebug() << "QServiceManager::interfaceDefault" << interfaceName;
889 d->setError(QServiceManager::NoError);
890 DatabaseManager::DbScope scope = d->scope == QService::SystemScope ?
891 DatabaseManager::SystemScope : DatabaseManager::UserScope;
892 QServiceInterfaceDescriptor info = d->dbManager->interfaceDefault(interfaceName, scope);
893 if (d->dbManager->lastError().code() != DBError::NoError) {
894 d->setError();
895 qDebug() << "error" << d->dbManager->lastError().text();
896 return QServiceInterfaceDescriptor();
897 }
898 return info;
899}
900
901/*!
902 Returns the type of error that last occurred.
903*/
904QServiceManager::Error QServiceManager::error() const
905{
906 return d->error;
907}
908
909/*!
910 \internal
911*/
912void QServiceManager::connectNotify(const QMetaMethod &signal)
913{
914 static const QMetaMethod serviceAddedSignal = QMetaMethod::fromSignal(signal: &QServiceManager::serviceAdded);
915 static const QMetaMethod serviceRemovedSignal = QMetaMethod::fromSignal(signal: &QServiceManager::serviceRemoved);
916 if (signal == serviceAddedSignal
917 || signal == serviceRemovedSignal) {
918 if (d->scope != QService::SystemScope)
919 d->dbManager->setChangeNotificationsEnabled(scope: DatabaseManager::UserScope, enabled: true);
920 d->dbManager->setChangeNotificationsEnabled(scope: DatabaseManager::SystemScope, enabled: true);
921 }
922}
923
924/*!
925 \internal
926*/
927void QServiceManager::disconnectNotify(const QMetaMethod &signal)
928{
929 static const QMetaMethod serviceAddedSignal = QMetaMethod::fromSignal(signal: &QServiceManager::serviceAdded);
930 static const QMetaMethod serviceRemovedSignal = QMetaMethod::fromSignal(signal: &QServiceManager::serviceRemoved);
931 if (signal == serviceAddedSignal
932 || signal == serviceRemovedSignal) {
933 if (!isSignalConnected(signal: serviceAddedSignal)
934 && !isSignalConnected(signal: serviceRemovedSignal)) {
935 if (d->scope != QService::SystemScope)
936 d->dbManager->setChangeNotificationsEnabled(scope: DatabaseManager::UserScope, enabled: false);
937 d->dbManager->setChangeNotificationsEnabled(scope: DatabaseManager::SystemScope, enabled: false);
938 }
939 }
940}
941
942bool QServiceManager::event(QEvent *e)
943{
944 if (e->type() == QEvent::ThreadChange) {
945 qWarning() << "QServiceManager CANNOT BE MOVED THREADS!";
946 }
947
948 return QObject::event(event: e);
949}
950
951#include "moc_qservicemanager.cpp"
952#include "qservicemanager.moc"
953QT_END_NAMESPACE
954
955

source code of qtsystems/src/serviceframework/qservicemanager.cpp