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 "qdeclarativeservice_p.h"
35
36#include <QQmlEngine>
37#include <QQmlInfo>
38
39#include <qservicereply.h>
40
41QT_BEGIN_NAMESPACE
42
43/*!
44 \qmltype ServiceLoader
45 \instantiates QDeclarativeService
46
47 \brief The ServiceLoader element holds an instance of a service object.
48 \inherits QObject
49 \inqmlmodule QtServiceFramework
50 \ingroup qml-serviceframework
51
52 The ServiceLoader element is part of the Qt ServiceFramework API and
53 provides a client instance of the service object. This element allows the specification of
54 the Service::interfaceName to locate the default service implemented at this interface.
55
56 To request a service more specifically, you can filter available ServiceDescriptors with
57 the ServiceFilter element, and then request service objects based off them.
58
59 Either way, the ServiceLoader element will provide you with the QtObject provided by that service
60 interface. You can then use its properties, signals, and slots as defined by its interface.
61
62 Example:
63 \code
64 import QtQuick 2.0
65 import QtServiceFramework 5.0
66
67 QtObject {
68 property alias serviceObject: service.serviceObject //In case you want to expose it upwards
69 ServiceLoader {
70 interfaceName: "com.qt.nokia.example.interface"
71 onStatusChanged: {
72 if (status == Service.Ready)
73 foo(serviceObject); //In case you want to do something with it as soon as it loads
74 else if (status == Service.Error)
75 errorHandling(errorString()); //In case you want to do error handling.
76 }
77 }
78 }
79 \endcode
80
81 \sa ServiceList
82*/
83QDeclarativeServiceLoader::QDeclarativeServiceLoader()
84 : m_serviceDescriptor(0), m_status(Null), m_asynchronous(true),
85 m_serviceObject(0), m_componentComplete(false), m_serviceManager(0),
86 m_serviceReply(0)
87{
88}
89
90QDeclarativeServiceLoader::~QDeclarativeServiceLoader()
91{
92 //Manager has qobject ownership
93 delete m_serviceObject;//QOBJECT parented?
94 delete m_serviceReply;
95}
96
97/*!
98 \qmlmethod string ServiceLoader::errorString()
99
100 This method returns a human readable description of the last error.
101
102 If the status is not ServiceLoader.Error, errorString() will return an empty string.
103*/
104/*!
105 \qmlproperty Status ServiceLoader::status
106
107 This property contains the status of the service object. It will be one of the following:
108
109 \list
110 \li ServiceLoader.Null - the service is inactive or no service has been set
111 \li ServiceLoader.Ready - the service has been loaded
112 \li ServiceLoader.Loading - the service is currently being loaded
113 \li ServiceLoader.Error - an error occurred while loading the service
114 \endlist
115
116 If you want to do something immediately after the service loads, the recommended route is
117 to monitor this property. For example:
118 \code
119 ServiceLoader {
120 onStatusChanged: {
121 if (status == ServiceLoader.Ready)
122 doStuffWith(serviceObject)
123 else if (status == ServiceLoader.Error)
124 console.debug(errorString())
125 }
126 }
127 \endcode
128
129*/
130/*!
131 \qmlproperty bool ServiceLoader::asynchronous
132
133 If asynchronous is false, then the element will block the main thread until a service object
134 is found or an error occurs. This will skip the Loading status. This is generally not
135 recommended, as blocking the main thread can lead to significant problems with user interface
136 responsiveness.
137
138 Default is true.
139*/
140/*!
141 \qmlproperty string ServiceLoader::interfaceName
142
143 Set this to select a service based off of the interface name. The service name,
144 and service version, will be selected for you if a match is found.
145*/
146/*!
147 \qmlproperty ServiceDescriptor* ServiceLoader::serviceDescriptor
148
149 Set this to select a specific service. ServiceDescriptors can be obtained
150 from the ServiceFilter element.
151
152*/
153
154
155/*!
156 \qmlproperty QObject* ServiceLoader::serviceObject
157
158 This property holds an instance of the service object which
159 can be used to make metaobject calls to the service.
160
161 serviceObject is only valid when the status property is set to
162 ServiceLoader.Ready. Otherwise, it should be a null reference.
163*/
164void QDeclarativeServiceLoader::componentComplete()
165{
166 if (!m_interfaceName.isEmpty() || m_serviceDescriptor)
167 startLoading();
168 m_componentComplete = true;
169}
170
171void QDeclarativeServiceLoader::startLoading()
172{
173 if (m_serviceReply) // Cancel pending requests
174 delete m_serviceReply; //Auto-disconnects signals
175
176 if (m_serviceObject) {
177 m_serviceObject->deleteLater();
178 m_serviceObject = 0;
179 emit serviceObjectChanged(arg: 0);// In sync only, you might get an extra signal
180 }
181
182 if (!m_serviceDescriptor && m_interfaceName.isEmpty()) { //Actually an 'unset' request
183 setStatus(Null);
184 return;
185 }
186
187 if (!m_serviceManager)
188 m_serviceManager = new QServiceManager(this);
189
190 if (m_asynchronous) {
191 if (m_serviceDescriptor)
192 m_serviceReply = m_serviceManager->loadInterfaceRequest(descriptor: *m_serviceDescriptor);
193 else
194 m_serviceReply = m_serviceManager->loadInterfaceRequest(interfaceName: m_interfaceName);
195 connect(sender: m_serviceReply, SIGNAL(finished()),
196 receiver: this, SLOT(finishLoading()));
197 setStatus(Loading);
198 } else {
199 finishLoading();
200 }
201}
202
203QString stringForError(QServiceManager::Error error)// ### Should we just expose the enum? Or merely pick better strings?
204{
205 switch (error) {
206 case QServiceManager::NoError: return QLatin1String("No error occurred.");
207 case QServiceManager::StorageAccessError: return QLatin1String("Storage access error.");
208 case QServiceManager::InvalidServiceLocation: return QLatin1String("Invalid service location.");
209 case QServiceManager::InvalidServiceXml: return QLatin1String("Invalid service XML.");
210 //case QService::InvalidInterfaceDescriptor: return QLatin1String("Invalid interface descriptor.");
211 case QServiceManager::PluginLoadingFailed: return QLatin1String("Error loading service plugin.");
212 case QServiceManager::ComponentNotFound: return QLatin1String("Service component not found.");
213 case QServiceManager::ServiceCapabilityDenied: return QLatin1String("You do not have permission to access this service capability.");
214 default: break;
215 }
216 return QLatin1String("Unknown error.");
217}
218
219void QDeclarativeServiceLoader::finishLoading()
220{
221 Q_ASSERT(m_serviceManager);
222 QServiceManager::Error error;
223 QObject* prevObject = m_serviceObject;
224 if (m_serviceReply) {
225 if (!m_serviceReply->isFinished())
226 return; //TODO: Evaluate/handle this error condition
227 error = m_serviceReply->error();
228 m_serviceObject = m_serviceReply->proxyObject();
229 m_serviceReply->deleteLater();
230 m_serviceReply = 0;
231 } else {
232 if (m_asynchronous)
233 qDebug() << "Uh oh..."; //TODO: Evaluate/handle this 'error' condition
234 if (m_serviceDescriptor)
235 m_serviceObject = m_serviceManager->loadInterface(descriptor: *m_serviceDescriptor);
236 else
237 m_serviceObject = m_serviceManager->loadInterface(interfaceName: m_interfaceName);
238 error = m_serviceManager->error();
239 }
240
241 if (error != QServiceManager::NoError) {
242 m_serviceObject = 0;
243 if (!m_asynchronous)
244 emit serviceObjectChanged(arg: 0);// In sync we didn't emit the intermediate state
245 m_errorString = stringForError(error);
246 setStatus(Error);
247 } else {
248 setStatus(Ready);
249 connect(sender: m_serviceObject, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)),
250 receiver: this, SLOT(IPCFault(QService::UnrecoverableIPCError)));
251 }
252
253 if (prevObject != m_serviceObject)
254 emit serviceObjectChanged(arg: m_serviceObject);
255
256 delete m_serviceManager;
257 m_serviceManager = 0;
258}
259
260void QDeclarativeServiceLoader::IPCFault(QService::UnrecoverableIPCError errorValue)
261{
262 switch (errorValue) {
263 default:
264 case QService::ErrorUnknown:
265 m_errorString = QLatin1String("IPC Error: Unkown Error");
266 break;
267 case QService::ErrorServiceNoLongerAvailable:
268 m_errorString = QLatin1String("IPC Error: Service no longer available");
269 break;
270 case QService::ErrorOutofMemory:
271 m_errorString = QLatin1String("IPC Error: Out of memory");
272 break;
273 case QService::ErrorPermissionDenied:
274 m_errorString = QLatin1String("IPC Error: Permission Denied");
275 break;
276 case QService::ErrorInvalidArguments:
277 m_errorString = QLatin1String("IPC Error: Invalid Arguments");
278 break;
279 }
280 setStatus(Error);
281 m_serviceObject->deleteLater();
282}
283
284/*!
285 \qmltype ServiceDescriptor
286 \instantiates QDeclarativeServiceDescriptor
287
288 \brief The ServiceDescriptor element holds a description of a service.
289 \inherits QObject
290
291 \ingroup qml-serviceframework
292
293 The ServiceDescriptor element is a simplified reflection of the ServiceDescriptor class,
294 and is used merely to contain data that can uniquely identify a service.
295
296 It cannot be created manually, use a ServiceList to search for service descriptions.
297
298 \sa ServiceList
299*/
300/*!
301 \qmlproperty QString ServiceDescriptor::serviceName
302
303 This property holds the interface name of the service.
304*/
305/*!
306 \qmlproperty QString ServiceDescriptor::interfaceName
307
308 This property holds the interface name of the service.
309*/
310
311/*!
312 \qmlproperty int ServiceDescriptor::majorVersion
313
314 This property holds the major version number of the service.
315*/
316/*!
317 \qmlproperty int ServiceDescriptor::minorVersion
318
319 This property holds the minor version number of the service.
320*/
321//TODO: Fix doc or remove property
322/*!
323 \qmlproperty bool ServiceDescriptor::valid
324
325 I have no clue what this property does, but it's probably important.
326*/
327/*!
328 \qmltype ServiceFilter
329 \instantiates QDeclarativeServiceFilter
330
331 \brief The ServiceFilter element holds a list of \l ServiceDescriptor objects.
332 \inherits QObject
333
334 \ingroup qml-serviceframework
335
336 The ServiceFilter element is part of the Qt ServiceFramework API and
337 provides a list of \l QServiceDesciptor objects at the interface ServiceFilter::interfaceName with
338 minimum version match ServiceFilter::minVersion properties. This list can be used to
339 select the desired service and instantiate a service object for access via the QMetaObject.
340
341 This element is a simplified reflection of the QServiceFilter class that provides a list
342 of ServiceDescriptors. Similarly, if the ServiceFilter::serviceName
343 and ServiceFilter::versionMatch are not provided they will respectively default to an empty
344 string with a minimum verison match.
345
346 Example:
347 \code
348 Item {
349 ServiceFilter {
350 id: serviceFilter
351 interfaceName: "com.qt.nokia.example.interface"
352 }
353 ServiceLoader {
354 serviceDescriptor: serviceFilter.serviceDescriptions[0] //To get the first matching service
355 }
356 Repeater{ //To instantiate an object for all matching services.
357 model: serviceFilter.serviceDescriptions
358 Item {
359 ServiceLoader {
360 serviceDescriptor: modelData
361 }
362 }
363 }
364 }
365 \endcode
366
367 \sa ServiceLoader ServiceDescriptor
368*/
369QDeclarativeServiceFilter::QDeclarativeServiceFilter(QObject* parent)
370 : QObject(parent),
371 m_majorVersion(1), // ### Is '1' the correct unset number?
372 m_minorVersion(0),
373 m_exactVersionMatching(false),
374 m_monitorServiceRegistrations(false),
375 m_serviceManager(0),
376 m_componentComplete(false)
377{
378}
379
380QDeclarativeServiceFilter::~QDeclarativeServiceFilter()
381{
382}
383/*!
384 \qmlproperty QString ServiceFilter::serviceName
385
386 This property holds the interface name of the services that
387 corresponds to setting QServiceFilter::setService().
388*/
389/*!
390 \qmlproperty QString ServiceFilter::interfaceName
391
392 This property holds the interface name of the services that
393 corresponds to setting QServiceFilter::setInterface().
394*/
395
396/*!
397 \qmlproperty int ServiceFilter::majorVersion
398
399 This property holds the major version number of the service filter that
400 corresponds to QServiceFilter::majorVersion().
401*/
402/*!
403 \qmlproperty int ServiceFilter::minorVersion
404
405 This property holds the minor version number of the service filter that
406 corresponds to QServiceFilter::minorVersion().
407*/
408
409/*!
410 \qmlproperty int ServiceFilter::monitorServiceRegistrations
411
412 This property controls the behaviour of the list when new services are
413 registered or deregistered. Setting this property to true means the list
414 will be automatically updated when a service is added or removed.
415
416 Continuing to monitor the services can be expensive, so it is recommended
417 that you only enable this feature if you need it.
418
419 Caution, your service descriptor object will be deleted if the service
420 is unregistered, even if the service is still running. This is primarily
421 of note if you are using the serviceDescriptions list as a model with
422 Service element delegates.
423*/
424
425/*!
426 \qmlproperty bool ServiceFilter::versionMatch
427
428 This property holds the version match rule of the service filter that
429 corresponds to QServiceFilter::versionMatchRule(). Within QML the values
430 ServiceFilter.Exact and ServiceFilter.Minimum correspond to
431 QServiceFilter::ExactVersionMatch and QServiceFilter::MinimumVersionMatch
432 respectively.
433*/
434
435void QDeclarativeServiceFilter::setMonitorServiceRegistrations(bool updates)
436{
437 if (m_monitorServiceRegistrations == updates)
438 return;
439
440 if (updates == false) {
441 disconnect(receiver: this, SLOT(servicesAddedRemoved()));
442 if (m_serviceManager)
443 delete m_serviceManager;
444 m_serviceManager = 0;
445 } else {
446 if (!m_serviceManager)
447 m_serviceManager = new QServiceManager(this);
448 connect(sender: m_serviceManager, SIGNAL(serviceAdded(QString,QService::Scope)),
449 receiver: this, SLOT(servicesAddedRemoved()));
450 connect(sender: m_serviceManager, SIGNAL(serviceRemoved(QString,QService::Scope)),
451 receiver: this, SLOT(servicesAddedRemoved()));
452 }
453
454 emit monitorServiceRegistrationsChanged(arg: updates);
455 m_monitorServiceRegistrations = updates;
456}
457
458void QDeclarativeServiceFilter::servicesAddedRemoved()
459{
460 // invoke in another event loop run ### Why?
461 QMetaObject::invokeMethod(obj: this, member: "updateServiceList", type: Qt::QueuedConnection);
462}
463
464QList<QDeclarativeServiceDescriptor> makeDeclarative(QList<QServiceInterfaceDescriptor> in)
465{
466 QList<QDeclarativeServiceDescriptor> out;
467 foreach (const QServiceInterfaceDescriptor &d, in)
468 out << QDeclarativeServiceDescriptor(d);
469 return out;
470}
471
472void QDeclarativeServiceFilter::updateServiceList()
473{
474 if (!m_componentComplete)
475 return;
476
477 if (!m_serviceManager)
478 m_serviceManager = new QServiceManager(this);
479 const QString version = QString::number(m_majorVersion) + "." + QString::number(m_minorVersion);
480
481 QServiceFilter filter;
482
483 if (!m_serviceName.isEmpty())
484 filter.setServiceName(m_serviceName);
485
486 if (!m_interfaceName.isEmpty())
487 filter.setInterface(interfaceName: m_interfaceName, version, rule: m_exactVersionMatching ?
488 QServiceFilter::ExactVersionMatch : QServiceFilter::MinimumVersionMatch);
489
490 QList<QDeclarativeServiceDescriptor> list = makeDeclarative(in: m_serviceManager->findInterfaces(filter));
491
492 if (list != m_services) {
493 m_services = list;
494 emit serviceDescriptionsChanged();
495 }
496
497 if (!m_monitorServiceRegistrations) {
498 delete m_serviceManager;
499 m_serviceManager = 0;
500 }
501
502}
503
504/*!
505 \qmlproperty QQmlListProperty ServiceFilter::serviceDescriptions
506
507 This property holds the list of \l ServiceDescriptor objects that match
508 the ServiceFilter::interfaceName and minimum ServiceFilter::versionNumber properties.
509*/
510
511void QDeclarativeServiceFilter::componentComplete()
512{
513 m_componentComplete = true;
514 updateServiceList();
515}
516
517void QDeclarativeServiceFilter::s_append(QQmlListProperty<QDeclarativeServiceDescriptor> *prop, QDeclarativeServiceDescriptor *service)
518{
519 QDeclarativeServiceFilter* list = static_cast<QDeclarativeServiceFilter*>(prop->object);
520 list->m_services.append(t: *service);//### This does not maintain the reference
521 list->serviceDescriptionsChanged();
522}
523int QDeclarativeServiceFilter::s_count(QQmlListProperty<QDeclarativeServiceDescriptor> *prop)
524{
525 return static_cast<QDeclarativeServiceFilter*>(prop->object)->m_services.count();
526}
527
528QDeclarativeServiceDescriptor* QDeclarativeServiceFilter::s_at(QQmlListProperty<QDeclarativeServiceDescriptor> *prop, int index)
529{
530 return &(static_cast<QDeclarativeServiceFilter*>(prop->object)->m_services[index]);
531}
532
533void QDeclarativeServiceFilter::s_clear(QQmlListProperty<QDeclarativeServiceDescriptor> *prop)
534{
535 QDeclarativeServiceFilter* list = static_cast<QDeclarativeServiceFilter*>(prop->object);
536 list->m_services.clear();
537 list->serviceDescriptionsChanged();
538}
539#include "moc_qdeclarativeservice_p.cpp"
540
541QT_END_NAMESPACE
542

source code of qtsystems/src/imports/serviceframework/qdeclarativeservice.cpp