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 | |
41 | QT_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 | */ |
83 | QDeclarativeServiceLoader::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 | |
90 | QDeclarativeServiceLoader::~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 | */ |
164 | void QDeclarativeServiceLoader::componentComplete() |
165 | { |
166 | if (!m_interfaceName.isEmpty() || m_serviceDescriptor) |
167 | startLoading(); |
168 | m_componentComplete = true; |
169 | } |
170 | |
171 | void 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 | |
203 | QString 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 | |
219 | void 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 | |
260 | void 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 | */ |
369 | QDeclarativeServiceFilter::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 | |
380 | QDeclarativeServiceFilter::~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 | |
435 | void 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 | |
458 | void QDeclarativeServiceFilter::servicesAddedRemoved() |
459 | { |
460 | // invoke in another event loop run ### Why? |
461 | QMetaObject::invokeMethod(obj: this, member: "updateServiceList" , type: Qt::QueuedConnection); |
462 | } |
463 | |
464 | QList<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 | |
472 | void 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 | |
511 | void QDeclarativeServiceFilter::componentComplete() |
512 | { |
513 | m_componentComplete = true; |
514 | updateServiceList(); |
515 | } |
516 | |
517 | void 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 | } |
523 | int QDeclarativeServiceFilter::s_count(QQmlListProperty<QDeclarativeServiceDescriptor> *prop) |
524 | { |
525 | return static_cast<QDeclarativeServiceFilter*>(prop->object)->m_services.count(); |
526 | } |
527 | |
528 | QDeclarativeServiceDescriptor* QDeclarativeServiceFilter::s_at(QQmlListProperty<QDeclarativeServiceDescriptor> *prop, int index) |
529 | { |
530 | return &(static_cast<QDeclarativeServiceFilter*>(prop->object)->m_services[index]); |
531 | } |
532 | |
533 | void 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 | |
541 | QT_END_NAMESPACE |
542 | |