1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 Ford Motor Company
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtRemoteObjects module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "private/qmetaobjectbuilder_p.h"
41
42 #include "qremoteobjectnode.h"
43 #include "qremoteobjectnode_p.h"
44
45 #include "qremoteobjectregistry.h"
46 #include "qremoteobjectdynamicreplica.h"
47 #include "qremoteobjectpacket_p.h"
48 #include "qremoteobjectregistrysource_p.h"
49 #include "qremoteobjectreplica_p.h"
50 #include "qremoteobjectsource_p.h"
51 #include "qremoteobjectabstractitemmodelreplica_p.h"
52 #include "qremoteobjectabstractitemmodeladapter_p.h"
53 #include <QtCore/qabstractitemmodel.h>
54 #include <memory>
55
56 QT_BEGIN_NAMESPACE
57
58 using namespace QtRemoteObjects;
59 using namespace QRemoteObjectStringLiterals;
60
61 using GadgetType = QVector<QVariant>;
62 using RegisteredType = QPair<GadgetType, std::shared_ptr<QMetaObject>>;
63 static QMutex s_managedTypesMutex;
64 static QHash<int, RegisteredType> s_managedTypes;
65 static QHash<int, QSet<IoDeviceBase*>> s_trackedConnections;
66
GadgetsStaticMetacallFunction(QObject * _o,QMetaObject::Call _c,int _id,void ** _a)67 static void GadgetsStaticMetacallFunction(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
68 {
69 if (_c == QMetaObject::ReadProperty) {
70 GadgetType *_t = reinterpret_cast<GadgetType *>(_o);
71 if (_id < _t->size()) {
72 const auto &prop = _t->at(_id);
73 QMetaType::destruct(int(prop.userType()), _a[0]);
74 QMetaType::construct(int(prop.userType()), _a[0], prop.constData());
75 }
76 } else if (_c == QMetaObject::WriteProperty) {
77 GadgetType *_t = reinterpret_cast<GadgetType *>(_o);
78 if (_id < _t->size()) {
79 auto & prop = (*_t)[_id];
80 prop = QVariant(prop.userType(), _a[0]);
81 }
82 }
83 }
84
GadgetTypedDestructor(int,void * ptr)85 static void GadgetTypedDestructor(int, void *ptr)
86 {
87 reinterpret_cast<GadgetType*>(ptr)->~GadgetType();
88 }
89
GadgetTypedConstructor(int type,void * where,const void * copy)90 static void *GadgetTypedConstructor(int type, void *where, const void *copy)
91 {
92 GadgetType *ret = where ? new(where) GadgetType : new GadgetType;
93 if (copy) {
94 *ret = *reinterpret_cast<const GadgetType*>(copy);
95 } else {
96 QMutexLocker lock(&s_managedTypesMutex);
97 auto it = s_managedTypes.find(type);
98 if (it == s_managedTypes.end()) {
99 delete ret;
100 return nullptr;
101 }
102 *ret = it->first;
103 }
104 return ret;
105 }
106
GadgetSaveOperator(QDataStream & out,const void * data)107 static void GadgetSaveOperator(QDataStream & out, const void *data)
108 {
109 const GadgetType *gadgetProperties = reinterpret_cast<const GadgetType *>(data);
110 for (const auto &prop : *gadgetProperties)
111 out << prop;
112 }
113
GadgetLoadOperator(QDataStream & in,void * data)114 static void GadgetLoadOperator(QDataStream &in, void *data)
115 {
116 GadgetType *gadgetProperties = reinterpret_cast<GadgetType *>(data);
117 for (auto &prop : *gadgetProperties)
118 in >> prop;
119 }
120
121 // Like the Q_GADGET static methods above, we need constructor/destructor methods
122 // in order to use dynamically defined enums with QVariant or as signal/slot
123 // parameters (i.e., the queued connection mechanism, which QtRO leverages).
124 //
125 // We will need the enum methods to support different sizes when typed scope enum
126 // support is added, so might as well use that now.
127 template<typename T>
EnumDestructor(void * ptr)128 static void EnumDestructor(void *ptr)
129 {
130 static_cast<T*>(ptr)->~T();
131 }
132
133 template<typename T>
EnumConstructor(void * where,const void * copy)134 static void *EnumConstructor(void *where, const void *copy)
135 {
136 T *ret = where ? new(where) T : new T;
137 if (copy)
138 *ret = *static_cast<const T*>(copy);
139 return ret;
140 }
141
142 // Not used, but keeping these in case we end up with a need for save/load.
143 template<typename T>
EnumSaveOperator(QDataStream & out,const void * data)144 static void EnumSaveOperator(QDataStream & out, const void *data)
145 {
146 const T value = *static_cast<const T *>(data);
147 out << value;
148 }
149
150 template<typename T>
EnumLoadOperator(QDataStream & in,void * data)151 static void EnumLoadOperator(QDataStream &in, void *data)
152 {
153 T value = *static_cast<T *>(data);
154 in >> value;
155 }
156
name(const QMetaObject * const mobj)157 static QString name(const QMetaObject * const mobj)
158 {
159 const int ind = mobj->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE);
160 return ind >= 0 ? QString::fromLatin1(mobj->classInfo(ind).value()) : QString();
161 }
162
getTypeNameAndMetaobjectFromClassInfo(const QMetaObject * & meta)163 QString QtRemoteObjects::getTypeNameAndMetaobjectFromClassInfo(const QMetaObject *& meta) {
164 QString typeName;
165 const int ind = meta->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE);
166 if (ind != -1) { //We have an object created from repc or at least with QCLASSINFO defined
167 typeName = QString::fromLatin1(meta->classInfo(ind).value());
168 while (true) {
169 Q_ASSERT(meta->superClass());//This recurses to QObject, which doesn't have QCLASSINFO_REMOTEOBJECT_TYPE
170 //At the point superclass doesn't have the same QCLASSINFO_REMOTEOBJECT_TYPE,
171 //we have the metaobject we should work from
172 if (ind != meta->superClass()->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE))
173 break;
174 meta = meta->superClass();
175 }
176 }
177 return typeName;
178 }
179
180 template <typename K, typename V, typename Query>
map_contains(const QMap<K,V> & map,const Query & key,typename QMap<K,V>::const_iterator & result)181 bool map_contains(const QMap<K,V> &map, const Query &key, typename QMap<K,V>::const_iterator &result)
182 {
183 const typename QMap<K,V>::const_iterator it = map.find(key);
184 if (it == map.end())
185 return false;
186 result = it;
187 return true;
188 }
189
190 /*!
191 \qmltype Node
192 \instantiates QRemoteObjectNode
193 \inqmlmodule QtRemoteObjects
194 \brief A node on a Qt Remote Objects network.
195
196 The Node type provides an entry point to a Qt Remote Objects network. A network
197 can be as simple as two nodes, or an arbitrarily complex set of processes and devices.
198
199 A Node does not have a url that other nodes can connect to, and thus is able to acquire
200 replicas only. It is not able to share source objects.
201
202 */
203
QRemoteObjectNodePrivate()204 QRemoteObjectNodePrivate::QRemoteObjectNodePrivate()
205 : QObjectPrivate()
206 , registry(nullptr)
207 , retryInterval(250)
208 , lastError(QRemoteObjectNode::NoError)
209 , persistedStore(nullptr)
210 { }
211
~QRemoteObjectNodePrivate()212 QRemoteObjectNodePrivate::~QRemoteObjectNodePrivate()
213 {
214 }
215
remoteObjectAddresses() const216 QRemoteObjectSourceLocations QRemoteObjectNodePrivate::remoteObjectAddresses() const
217 {
218 if (registry)
219 return registry->sourceLocations();
220 return QRemoteObjectSourceLocations();
221 }
222
remoteObjectAddresses() const223 QRemoteObjectSourceLocations QRemoteObjectRegistryHostPrivate::remoteObjectAddresses() const
224 {
225 if (registrySource)
226 return registrySource->sourceLocations();
227 return QRemoteObjectSourceLocations();
228 }
229
230 /*!
231 \reimp
232 */
timerEvent(QTimerEvent *)233 void QRemoteObjectNode::timerEvent(QTimerEvent*)
234 {
235 Q_D(QRemoteObjectNode);
236
237 for (auto it = d->pendingReconnect.begin(), end = d->pendingReconnect.end(); it != end; /*erasing*/) {
238 const auto &conn = *it;
239 if (conn->isOpen()) {
240 it = d->pendingReconnect.erase(it);
241 } else {
242 conn->connectToServer();
243 ++it;
244 }
245 }
246
247 if (d->pendingReconnect.isEmpty())
248 d->reconnectTimer.stop();
249
250 qRODebug(this) << "timerEvent" << d->pendingReconnect.size();
251 }
252
253 /*!
254 \qmlproperty int Node::heartbeatInterval
255
256 Heartbeat interval in ms.
257
258 The heartbeat (only helpful for socket connections) will periodically send a
259 message to connected nodes to detect whether the connection was disrupted.
260 Qt Remote Objects will try to reconnect automatically if it detects a dropped
261 connection. This function can help with that detection since the client will
262 only detect that the server is unavailable when it tries to send data.
263
264 A value of \c 0 (the default) will disable the heartbeat.
265 */
266
267
268 /*!
269 \property QRemoteObjectNode::heartbeatInterval
270 \brief Heartbeat interval in ms.
271
272 The heartbeat (only helpful for socket connections) will periodically send a
273 message to connected nodes to detect whether the connection was disrupted.
274 Qt Remote Objects will try to reconnect automatically if it detects a dropped
275 connection. This function can help with that detection since the client will
276 only detect that the server is unavailable when it tries to send data.
277
278 A value of \c 0 (the default) will disable the heartbeat.
279 */
heartbeatInterval() const280 int QRemoteObjectNode::heartbeatInterval() const
281 {
282 Q_D(const QRemoteObjectNode);
283 return d->m_heartbeatInterval;
284 }
285
setHeartbeatInterval(int interval)286 void QRemoteObjectNode::setHeartbeatInterval(int interval)
287 {
288 Q_D(QRemoteObjectNode);
289 if (d->m_heartbeatInterval == interval)
290 return;
291 d->m_heartbeatInterval = interval;
292 emit heartbeatIntervalChanged(interval);
293 }
294
295 /*!
296 \since 5.12
297 \typedef QRemoteObjectNode::RemoteObjectSchemaHandler
298
299 Typedef for a std::function method that can take a QUrl input and is
300 responsible for creating the communications channel between this node and
301 the node hosting the desired \l Source. As some types of QIODevices (e.g.,
302 QSslSocket) require additional steps before the device is ready for use,
303 the method is responsible for calling \l addClientSideConnection once the
304 connection is fully established.
305 */
306
307 /*!
308 \since 5.12
309 \brief Provide a custom method to handle externally provided schemas
310
311 This method is tied to the \l Registry and \l {External Schemas}. By
312 registering a std::function handler for an external schema, the registered
313 method will be called when the registry is notified of a \l Source you've
314 acquired being available. Without this registration, QtRO would only be
315 able to handle the "built-in" schemas.
316
317 The provided method, \a handler, will be called when the registry sees a \l
318 Source object on a new (not yet connected) Node with a {QUrl::schema()} of
319 \a schema. The \a handler, of type \l
320 QRemoteObjectNode::RemoteObjectSchemaHandler will get the \l QUrl of the
321 Node providing the \l Source as an input parameter, and is responsible for
322 establishing the communications channel (a \l QIODevice of some sort) and
323 calling \l addClientSideConnection with it.
324
325 \sa RemoteObjectSchemaHandler
326 */
registerExternalSchema(const QString & schema,QRemoteObjectNode::RemoteObjectSchemaHandler handler)327 void QRemoteObjectNode::registerExternalSchema(const QString &schema, QRemoteObjectNode::RemoteObjectSchemaHandler handler)
328 {
329 Q_D(QRemoteObjectNode);
330 d->schemaHandlers.insert(schema, handler);
331 }
332
333 /*!
334 \since 5.11
335 \brief Forward Remote Objects from another network
336
337 The proxy functionality is useful when you want to share \l Source objects
338 over multiple networks. For instance, if you have an embedded target using
339 target-only connections (like local) and you want to make some of those
340 same objects available externally.
341
342 As a concrete example, say you have a set of processes talking to each
343 other on your target hardware using a registry, with the \l Registry at
344 "local:registry" and separate processes using a node at "local:MyHost" that
345 holds \l Source objects. If you wanted to access these objects, but over
346 tcp, you could create a new proxyNode like so:
347
348 \code
349 // myInternalHost is a node only visible on the device...
350 QRemoteObjectHost myInternalHost("local:MyHost");
351 myInternalHost.enableRemoting<SomeObject>(&someObject);
352
353 // Regular host node, listening on port 12123, so visible to other
354 // devices
355 QRemoteObjectHost proxyNode("tcp://localhost:12123");
356
357 // Enable proxying objects from nodes on the local machine's internal
358 // QtRO bus
359 proxyNode.proxy("local:registry");
360 \endcode
361
362 And from another device you create another node:
363
364 \code
365 // NB: localhost resolves to a different ip address than proxyNode
366 QRemoteObjectHost nodeOnRemoteDevice("tcp://localhost:23234");
367
368 // Connect to the target's proxyNode directly, or use a tcp registry...
369 nodeOnRemoteDevice.connectToNode("tcp://<target device>:12123");
370
371 // Because of the proxy, we can get the object over tcp/ip port 12123,
372 // even though we can't connect directly to "local:MyHost"
373 SomeObject *so = nodeOnRemoteDevice.acquire<SomeObject>();
374 \endcode
375
376 This would (internally) create a node in proxyNode, which (again
377 internally/automatically) connects to the provided registry (given by the
378 \a registryUrl parameter, "local:registry" in this example). Whenever
379 local:registry emits the \l remoteObjectAdded signal, the
380 \c QRemoteObjectSourceLocation is passed to the \a filter given to the proxy
381 call. If this method returns true (the default filter simply returns true
382 without any filtering), the object is acquired() from the internal node and
383 enableRemoting() (once the replica is initialized) is called on proxyNode.
384
385 If a \a hostUrl is provided (which is required to enable reverseProxy, but
386 not needed otherwise), the internal node will be a \l QRemoteObjectHost node
387 configured with the provided address. If no \a hostUrl is provided, the
388 internal node will be a QRemoteObjectNode (not HostNode).
389
390 Returns \c true if the object is acquired from the internal node.
391
392 \sa reverseProxy()
393 */
proxy(const QUrl & registryUrl,const QUrl & hostUrl,RemoteObjectNameFilter filter)394 bool QRemoteObjectHostBase::proxy(const QUrl ®istryUrl, const QUrl &hostUrl, RemoteObjectNameFilter filter)
395 {
396 Q_D(QRemoteObjectHostBase);
397 if (!registryUrl.isValid() || !QtROClientFactory::instance()->isValid(registryUrl)) {
398 qROWarning(this) << "Can't proxy to registryUrl (invalid url or schema)" << registryUrl;
399 return false;
400 }
401
402 if (!hostUrl.isEmpty() && !QtROClientFactory::instance()->isValid(hostUrl)) {
403 qROWarning(this) << "Can't proxy using hostUrl (invalid schema)" << hostUrl;
404 return false;
405 }
406
407 if (d->proxyInfo) {
408 qROWarning(this) << "Proxying from multiple objects is currently not supported.";
409 return false;
410 }
411
412 QRemoteObjectNode *node;
413 if (hostUrl.isEmpty()) {
414 node = new QRemoteObjectNode(registryUrl);
415 } else {
416 node = new QRemoteObjectHost(hostUrl, registryUrl);
417 }
418 d->proxyInfo = new ProxyInfo(node, this, filter);
419 return true;
420 }
421
422 /*!
423 \since 5.11
424 \brief Forwards remote objects to another network.
425
426 The reverseProxy() function allows the \l proxy() functionality to be
427 extended, in effect mirroring the proxy functionality in the "reverse"
428 direction. These are distinct, because node communication is not symmetric,
429 one side calls enableRemoting() with a \l Source object, the other side
430 calls acquire() to get a \l Replica. Using \l proxy() allows you to
431 "observe" objects on a target device remotely via acquire, but it does not
432 allow off-target \l Source objects to be acquired from the device's local:*
433 network. That is where \l reverseProxy() comes in. If a proxyNode is
434 created like so:
435
436 \code
437 // myInternalHost is a node only visible on the device...
438 QRemoteObjectHost myInternalHost("local:MyHost");
439
440 // RegistryHost node, listening on port 12123, so visible to other
441 // devices. The node must be a RegistryHost, so the Sources on
442 // the "outside" network can be forwarded to the inner network.
443 QRemoteObjectRegistryHost proxyNode("tcp://localhost:12123");
444
445 // Enable proxying objects from nodes on the local machine's internal
446 // QtRO bus. Note the hostUrl parameter is now needed.
447 proxyNode.proxy("local:registry", "local:fromProxy");
448 proxyNode.reverseProxy();
449 \endcode
450
451 And from another device you create another node:
452
453 \code
454 // NB: localhost resolves to a different ip address than proxyNode
455 QRemoteObjectHost nodeOnRemoteDevice("tcp://localhost:23234");
456
457 // Connect to the target's proxyNode directly, or use a tcp registry...
458 nodeOnRemoteDevice.connectToNode("tcp://<target device>:12123");
459
460 // Because of the reverseProxy, we can expose objects on this device
461 // and they will make their way to proxyNode...
462 nodeOnRemoteDevice.enableRemoting<OtherObject>(&otherObject);
463 \endcode
464
465 \code
466 // Acquire() can now see the objects on other devices through proxyNode,
467 // due to the reverseProxy call.
468 OtherObject *oo = myInternalHost.acquire<OtherObject>();
469 \endcode
470
471 While the \l proxy() functionality allows \l Source objects on another
472 network to be acquired(), reverseProxy() allows \l Source objects to be
473 "pushed" to an otherwise inaccessible network.
474
475 \note proxy() needs to be called before \l reverseProxy(), and a
476 hostUrl needs to be provided to \l proxy for \l reverseProxy() to work. The
477 \l reverseProxy() method allows a separate \a filter to be applied. This
478 reverseProxy specific filter will receive notifications of new \l Source
479 objects on proxyNode and acquire them on the internal node if they pass the
480 reverseFilter.
481
482 Returns \c true on success, \c false otherwise.
483
484 \sa proxy()
485 */
reverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter)486 bool QRemoteObjectHostBase::reverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter)
487 {
488 Q_D(QRemoteObjectHostBase);
489
490 if (!d->proxyInfo) {
491 qROWarning(this) << "proxy() needs to be called before setting up reverse proxy.";
492 return false;
493 }
494
495 QRemoteObjectHost *host = qobject_cast<QRemoteObjectHost *>(d->proxyInfo->proxyNode);
496 if (!host) {
497 qROWarning(this) << "proxy() needs called with host-url to enable reverse proxy.";
498 return false;
499 }
500
501 return d->proxyInfo->setReverseProxy(filter);
502 }
503
504 /*!
505 \internal The replica needs to have a default constructor to be able
506 to create a replica from QML. In order for it to be properly
507 constructed, there needs to be a way to associate the replica with a
508 node and start the replica initialization. Thus we need a public
509 method on node to facilitate that. That's initializeReplica.
510 */
initializeReplica(QRemoteObjectReplica * instance,const QString & name)511 void QRemoteObjectNode::initializeReplica(QRemoteObjectReplica *instance, const QString &name)
512 {
513 Q_D(QRemoteObjectNode);
514 if (instance->inherits("QRemoteObjectDynamicReplica")) {
515 d->setReplicaImplementation(nullptr, instance, name);
516 } else {
517 const QMetaObject *meta = instance->metaObject();
518 // This is a templated acquire, so we tell the Source we don't need
519 // them to send the class definition. Thus we need to store the
520 // metaObject for this class - if this is a nested class, the QObject
521 // could be a nullptr or updated from the source,
522 d->dynamicTypeManager.addFromMetaObject(meta);
523 d->setReplicaImplementation(meta, instance, name.isEmpty() ? ::name(meta) : name);
524 }
525 }
526
setLastError(QRemoteObjectNode::ErrorCode errorCode)527 void QRemoteObjectNodePrivate::setLastError(QRemoteObjectNode::ErrorCode errorCode)
528 {
529 Q_Q(QRemoteObjectNode);
530 lastError = errorCode;
531 emit q->error(lastError);
532 }
533
setReplicaImplementation(const QMetaObject * meta,QRemoteObjectReplica * instance,const QString & name)534 void QRemoteObjectNodePrivate::setReplicaImplementation(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name)
535 {
536 qROPrivDebug() << "Starting setReplicaImplementation for" << name;
537 openConnectionIfNeeded(name);
538 QMutexLocker locker(&mutex);
539 if (hasInstance(name)) {
540 qCDebug(QT_REMOTEOBJECT)<<"setReplicaImplementation - using existing instance";
541 QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(name).toStrongRef());
542 Q_ASSERT(rep);
543 instance->d_impl = rep;
544 rep->configurePrivate(instance);
545 } else {
546 instance->d_impl.reset(handleNewAcquire(meta, instance, name));
547 instance->initialize();
548 replicas.insert(name, instance->d_impl.toWeakRef());
549 qROPrivDebug() << "setReplicaImplementation - Created new instance" << name<<remoteObjectAddresses();
550 }
551 }
552
553 /*!
554 Returns a pointer to the Node's \l {QRemoteObjectRegistry}, if the Node
555 is using the Registry feature; otherwise it returns 0.
556 */
registry() const557 const QRemoteObjectRegistry *QRemoteObjectNode::registry() const
558 {
559 Q_D(const QRemoteObjectNode);
560 return d->registry;
561 }
562
563 /*!
564 \class QRemoteObjectAbstractPersistedStore
565 \inmodule QtRemoteObjects
566 \brief A class which provides the methods for setting PROP values of a
567 replica to value they had the last time the replica was used.
568
569 This can be used to provide a "reasonable" value to be displayed until the
570 connection to the source is established and current values are available.
571
572 This class must be overridden to provide an implementation for saving (\l
573 QRemoteObjectAbstractPersistedStore::saveProperties) and restoring (\l
574 QRemoteObjectAbstractPersistedStore::restoreProperties) PROP values. The derived
575 type can then be set for a node, and any replica acquired from that node
576 will then automatically store PERSISTED properties when the replica
577 destructor is called, and retrieve the values when the replica is
578 instantiated.
579 */
580
581 /*!
582 Constructs a QRemoteObjectAbstractPersistedStore with the given \a parent.
583 The default value of \a parent is \c nullptr.
584 */
QRemoteObjectAbstractPersistedStore(QObject * parent)585 QRemoteObjectAbstractPersistedStore::QRemoteObjectAbstractPersistedStore(QObject *parent)
586 : QObject(parent)
587 {
588 }
589
~QRemoteObjectAbstractPersistedStore()590 QRemoteObjectAbstractPersistedStore::~QRemoteObjectAbstractPersistedStore()
591 {
592 }
593
594 /*!
595 \fn virtual void QRemoteObjectAbstractPersistedStore::saveProperties(const QString &repName, const QByteArray &repSig, const QVariantList &values)
596
597 This method will be provided the replica class's \a repName, \a repSig and the list of
598 \a values that PERSISTED properties have when the replica destructor was
599 called. It is the responsibility of the inheriting class to store the
600 information in a manner consistent for \l
601 QRemoteObjectAbstractPersistedStore::restoreProperties to retrieve.
602
603 \sa QRemoteObjectAbstractPersistedStore::restoreProperties
604 */
605
606 /*!
607 \fn virtual QVariantList QRemoteObjectAbstractPersistedStore::restoreProperties(const QString &repName, const QByteArray &repSig)
608
609 This method will be provided the replica class's \a repName and \a repSig when the
610 replica is being initialized. It is the responsibility of the inheriting
611 class to get the last values persisted by \l
612 QRemoteObjectAbstractPersistedStore::saveProperties and return them. An empty
613 QVariantList should be returned if no values are available.
614
615 \sa QRemoteObjectAbstractPersistedStore::saveProperties
616 */
617
618
persistedStore() const619 QRemoteObjectAbstractPersistedStore *QRemoteObjectNode::persistedStore() const
620 {
621 Q_D(const QRemoteObjectNode);
622 return d->persistedStore;
623 }
624
625 /*!
626 \qmlproperty QRemoteObjectAbstractPersistedStore Node::persistedStore
627
628 Allows setting a \l QRemoteObjectAbstractPersistedStore instance for the node.
629
630 Allows replica \l PROP members with the PERSISTED trait to save their current value when the
631 replica is deleted and restore a stored value the next time the replica is started.
632
633 Requires a \l QRemoteObjectAbstractPersistedStore class implementation to control where and how
634 persistence is handled. A default QSettings-based implementation is provided by SettingsStore.
635 */
636
637 /*!
638 \since 5.11
639 \property QRemoteObjectNode::persistedStore
640 \brief Allows setting a \l QRemoteObjectAbstractPersistedStore instance for the node.
641
642 Allows replica \l PROP members with the PERSISTED trait to save their current value when the
643 replica is deleted and restore a stored value the next time the replica is started.
644
645 Requires a \l QRemoteObjectAbstractPersistedStore class implementation to control where and how
646 persistence is handled.
647 */
setPersistedStore(QRemoteObjectAbstractPersistedStore * persistedStore)648 void QRemoteObjectNode::setPersistedStore(QRemoteObjectAbstractPersistedStore *persistedStore)
649 {
650 Q_D(QRemoteObjectNode);
651 d->persistedStore = persistedStore;
652 }
653
QRemoteObjectAbstractPersistedStore(QRemoteObjectAbstractPersistedStorePrivate & dptr,QObject * parent)654 QRemoteObjectAbstractPersistedStore::QRemoteObjectAbstractPersistedStore(QRemoteObjectAbstractPersistedStorePrivate &dptr, QObject *parent)
655 : QObject(dptr, parent)
656 {
657 }
658
QRemoteObjectAbstractPersistedStorePrivate()659 QRemoteObjectAbstractPersistedStorePrivate::QRemoteObjectAbstractPersistedStorePrivate()
660 {
661 }
662
~QRemoteObjectAbstractPersistedStorePrivate()663 QRemoteObjectAbstractPersistedStorePrivate::~QRemoteObjectAbstractPersistedStorePrivate()
664 {
665 }
666
~QRemoteObjectMetaObjectManager()667 QRemoteObjectMetaObjectManager::~QRemoteObjectMetaObjectManager()
668 {
669 for (QMetaObject *mo : dynamicTypes)
670 free(mo); //QMetaObjectBuilder uses malloc, not new
671 }
672
metaObjectForType(const QString & type)673 const QMetaObject *QRemoteObjectMetaObjectManager::metaObjectForType(const QString &type)
674 {
675 qCDebug(QT_REMOTEOBJECT) << "metaObjectForType: looking for" << type << "static keys:" << staticTypes.keys() << "dynamic keys:" << dynamicTypes.keys();
676 Q_ASSERT(staticTypes.contains(type) || dynamicTypes.contains(type));
677 auto it = staticTypes.constFind(type);
678 if (it != staticTypes.constEnd())
679 return it.value();
680 return dynamicTypes.value(type);
681 }
682
trackConnection(int typeId,IoDeviceBase * connection)683 static void trackConnection(int typeId, IoDeviceBase *connection)
684 {
685 QMutexLocker lock(&s_managedTypesMutex);
686 if (s_trackedConnections[typeId].contains(connection))
687 return;
688 s_trackedConnections[typeId].insert(connection);
689 auto unregisterIfNotUsed = [typeId, connection]{
690 QMutexLocker lock(&s_managedTypesMutex);
691 Q_ASSERT(s_trackedConnections.contains(typeId));
692 Q_ASSERT(s_trackedConnections[typeId].contains(connection));
693 s_trackedConnections[typeId].remove(connection);
694 if (s_trackedConnections[typeId].isEmpty()) {
695 s_trackedConnections.remove(typeId);
696 s_managedTypes.remove(typeId);
697 QMetaType::unregisterType(typeId);
698 }
699 };
700
701 // Unregister the type only when the connection is destroyed
702 // Do not unregister types when the connections is discconected, because
703 // if it gets reconnected it will not register the types again
704 QObject::connect(connection, &IoDeviceBase::destroyed, unregisterIfNotUsed);
705 }
706
707 struct EnumPair {
708 QByteArray name;
709 int value;
710 };
711
712 struct EnumData {
713 QByteArray name;
714 bool isFlag, isScoped;
715 quint32 keyCount, size;
716 QVector<EnumPair> values;
717 };
718
719 struct GadgetProperty {
720 QByteArray name;
721 QByteArray type;
722 };
723
724 struct GadgetData {
725 QVector<GadgetProperty> properties;
726 QVector<EnumData> enums;
727 };
728
729 using Gadgets = QHash<QByteArray, GadgetData>;
730
registerEnum(const QByteArray & name,const QMetaObject * meta,int size=4)731 static void registerEnum(const QByteArray &name, const QMetaObject *meta, int size=4)
732 {
733 // When we add support for enum classes, we will need to set this to something like
734 // QByteArray(enumClass).append("::").append(enumMeta.name()) when enumMeta.isScoped() is true.
735 // That is a new feature, though.
736 if (QMetaType::isRegistered(QMetaType::type(name)))
737 return;
738 static const auto flags = QMetaType::IsEnumeration | QMetaType::NeedsConstruction | QMetaType::NeedsDestruction;
739 int id;
740 switch (size) {
741 case 1: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint8>,
742 &EnumConstructor<qint8>, size, flags, meta);
743 break;
744 case 2: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint16>,
745 &EnumConstructor<qint16>, size, flags, meta);
746 break;
747 case 4: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint32>,
748 &EnumConstructor<qint32>, size, flags, meta);
749 break;
750 // Qt currently only supports enum values of 4 or less bytes (QMetaEnum value(index) returns int)
751 // case 8: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint64>,
752 // &EnumConstructor<qint64>, size, flags, meta);
753 // break;
754 default:
755 qWarning() << "Invalid enum detected" << name << "with size" << size << ". Defaulting to register as int.";
756 size = 4;
757 id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint32>,
758 &EnumConstructor<qint32>, size, flags, meta);
759 }
760 #ifdef QTRO_VERBOSE_PROTOCOL
761 qDebug() << "Registering new enum with id" << id << name << "size:" << size;
762 #endif
763 qCDebug(QT_REMOTEOBJECT) << "Registering new enum with id" << id << name << "size:" << size;
764 }
765
registerGadgets(IoDeviceBase * connection,Gadgets & gadgets,QByteArray typeName)766 static int registerGadgets(IoDeviceBase *connection, Gadgets &gadgets, QByteArray typeName)
767 {
768 const auto &gadget = gadgets.take(typeName);
769 int typeId = QMetaType::type(typeName);
770 if (typeId != QMetaType::UnknownType) {
771 trackConnection(typeId, connection);
772 return typeId;
773 }
774
775 QMetaObjectBuilder gadgetBuilder;
776 gadgetBuilder.setClassName(typeName);
777 gadgetBuilder.setFlags(QMetaObjectBuilder::DynamicMetaObject | QMetaObjectBuilder::PropertyAccessInStaticMetaCall);
778 GadgetType gadgetType;
779 for (const auto &prop : gadget.properties) {
780 int propertyType = QMetaType::type(prop.type);
781 if (!propertyType && gadgets.contains(prop.type))
782 propertyType = registerGadgets(connection, gadgets, prop.type);
783 gadgetType.push_back(QVariant(QVariant::Type(propertyType)));
784 auto dynamicProperty = gadgetBuilder.addProperty(prop.name, prop.type);
785 dynamicProperty.setWritable(true);
786 dynamicProperty.setReadable(true);
787 }
788 for (const auto &enumData: gadget.enums) {
789 auto enumBuilder = gadgetBuilder.addEnumerator(enumData.name);
790 enumBuilder.setIsFlag(enumData.isFlag);
791 enumBuilder.setIsScoped(enumData.isScoped);
792
793 for (quint32 k = 0; k < enumData.keyCount; ++k) {
794 const auto pair = enumData.values.at(k);
795 enumBuilder.addKey(pair.name, pair.value);
796 }
797 }
798 auto meta = gadgetBuilder.toMetaObject();
799 const auto enumCount = meta->enumeratorCount();
800 for (int i = 0; i < enumCount; i++) {
801 const QByteArray registeredName = QByteArray(typeName).append("::").append(meta->enumerator(i).name());
802 registerEnum(registeredName, meta, gadget.enums.at(i).size);
803 }
804 QMetaType::TypeFlags flags = QMetaType::IsGadget;
805 int gadgetTypeId;
806 if (meta->propertyCount()) {
807 meta->d.static_metacall = &GadgetsStaticMetacallFunction;
808 meta->d.superdata = nullptr;
809 flags |= QMetaType::NeedsConstruction | QMetaType::NeedsDestruction;
810 gadgetTypeId = QMetaType::registerType(typeName.constData(),
811 &GadgetTypedDestructor,
812 &GadgetTypedConstructor,
813 sizeof(GadgetType),
814 flags, meta);
815 QMetaType::registerStreamOperators(gadgetTypeId, &GadgetSaveOperator, &GadgetLoadOperator);
816 } else {
817 gadgetTypeId = QMetaType::registerType(typeName.constData(),
818 nullptr,
819 nullptr,
820 sizeof(GadgetType),
821 flags, meta);
822 }
823 trackConnection(gadgetTypeId, connection);
824 QMutexLocker lock(&s_managedTypesMutex);
825 s_managedTypes[gadgetTypeId] = qMakePair(gadgetType, std::shared_ptr<QMetaObject>{meta, [](QMetaObject *ptr){ ::free(ptr); }});
826 return gadgetTypeId;
827 }
828
registerAllGadgets(IoDeviceBase * connection,Gadgets & gadgets)829 static void registerAllGadgets(IoDeviceBase *connection, Gadgets &gadgets)
830 {
831 while (!gadgets.isEmpty())
832 registerGadgets(connection, gadgets, gadgets.constBegin().key());
833 }
834
deserializeEnum(QDataStream & ds,EnumData & enumData)835 static void deserializeEnum(QDataStream &ds, EnumData &enumData)
836 {
837 ds >> enumData.name;
838 ds >> enumData.isFlag;
839 ds >> enumData.isScoped;
840 ds >> enumData.size;
841 ds >> enumData.keyCount;
842 for (quint32 i = 0; i < enumData.keyCount; i++) {
843 EnumPair pair;
844 ds >> pair.name;
845 ds >> pair.value;
846 enumData.values.push_back(pair);
847 }
848 }
849
parseGadgets(IoDeviceBase * connection,QDataStream & in)850 static void parseGadgets(IoDeviceBase *connection, QDataStream &in)
851 {
852 quint32 qtEnums, numGadgets;
853 in >> qtEnums; // Qt enums - just need registration
854 for (quint32 i = 0; i < qtEnums; ++i) {
855 QByteArray enumName;
856 in >> enumName;
857 QMetaType t(QMetaType::type(enumName.constData()));
858 registerEnum(enumName, t.metaObject()); // All Qt enums have default type int
859 }
860 in >> numGadgets;
861 if (numGadgets == 0)
862 return;
863 Gadgets gadgets;
864 for (quint32 i = 0; i < numGadgets; ++i) {
865 QByteArray type;
866 in >> type;
867 quint32 numProperties, numEnums;
868 in >> numProperties;
869 auto &properties = gadgets[type].properties;
870 for (quint32 p = 0; p < numProperties; ++p) {
871 GadgetProperty prop;
872 in >> prop.name;
873 in >> prop.type;
874 properties.push_back(prop);
875 }
876 in >> numEnums;
877 auto &enums = gadgets[type].enums;
878 for (quint32 e = 0; e < numEnums; ++e) {
879 EnumData enumData;
880 deserializeEnum(in, enumData);
881 enums.push_back(enumData);
882 }
883 }
884 registerAllGadgets(connection, gadgets);
885 }
886
addDynamicType(IoDeviceBase * connection,QDataStream & in)887 QMetaObject *QRemoteObjectMetaObjectManager::addDynamicType(IoDeviceBase *connection, QDataStream &in)
888 {
889 QMetaObjectBuilder builder;
890 builder.setSuperClass(&QRemoteObjectReplica::staticMetaObject);
891 builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
892
893 QString typeString;
894 QByteArray type;
895 quint32 numEnums = 0;
896 quint32 numSignals = 0;
897 quint32 numMethods = 0;
898 quint32 numProperties = 0;
899
900 in >> typeString;
901 type = typeString.toLatin1();
902 builder.addClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE, type);
903 builder.setClassName(type);
904
905 in >> numEnums;
906 QVector<quint32> enumSizes(numEnums);
907 for (quint32 i = 0; i < numEnums; ++i) {
908 EnumData enumData;
909 deserializeEnum(in, enumData);
910 auto enumBuilder = builder.addEnumerator(enumData.name);
911 enumBuilder.setIsFlag(enumData.isFlag);
912 enumBuilder.setIsScoped(enumData.isScoped);
913 enumSizes[i] = enumData.size;
914
915 for (quint32 k = 0; k < enumData.keyCount; ++k) {
916 const auto pair = enumData.values.at(k);
917 enumBuilder.addKey(pair.name, pair.value);
918 }
919 }
920 parseGadgets(connection, in);
921
922 int curIndex = 0;
923
924 in >> numSignals;
925 for (quint32 i = 0; i < numSignals; ++i) {
926 QByteArray signature;
927 QList<QByteArray> paramNames;
928 in >> signature;
929 in >> paramNames;
930 ++curIndex;
931 auto mmb = builder.addSignal(signature);
932 mmb.setParameterNames(paramNames);
933 }
934
935 in >> numMethods;
936 for (quint32 i = 0; i < numMethods; ++i) {
937 QByteArray signature, returnType;
938 QList<QByteArray> paramNames;
939 in >> signature;
940 in >> returnType;
941 in >> paramNames;
942 ++curIndex;
943 const bool isVoid = returnType.isEmpty() || returnType == QByteArrayLiteral("void");
944 QMetaMethodBuilder mmb;
945 if (isVoid)
946 mmb = builder.addMethod(signature);
947 else
948 mmb = builder.addMethod(signature, QByteArrayLiteral("QRemoteObjectPendingCall"));
949 mmb.setParameterNames(paramNames);
950 }
951
952 in >> numProperties;
953
954 for (quint32 i = 0; i < numProperties; ++i) {
955 QByteArray name;
956 QByteArray typeName;
957 QByteArray signalName;
958 in >> name;
959 in >> typeName;
960 in >> signalName;
961 if (signalName.isEmpty())
962 builder.addProperty(name, typeName);
963 else
964 builder.addProperty(name, typeName, builder.indexOfSignal(signalName));
965 }
966
967 auto meta = builder.toMetaObject();
968 // Our type likely has enumerations from the inherited base classes, such as the Replica State
969 // We only want to register the new enumerations, and since we just added them, we know they
970 // are the last indices. Thus a backwards count seems most efficient.
971 const int totalEnumCount = meta->enumeratorCount();
972 int incrementingIndex = 0;
973 for (int i = numEnums; i > 0; i--) {
974 auto const enumMeta = meta->enumerator(totalEnumCount - i);
975 const QByteArray registeredName = QByteArray(type).append("::").append(enumMeta.name());
976 registerEnum(registeredName, meta, enumSizes.at(incrementingIndex++));
977 }
978 dynamicTypes.insert(typeString, meta);
979 return meta;
980 }
981
addFromMetaObject(const QMetaObject * metaObject)982 void QRemoteObjectMetaObjectManager::addFromMetaObject(const QMetaObject *metaObject)
983 {
984 QString className = QLatin1String(metaObject->className());
985 if (!className.endsWith(QLatin1String("Replica")))
986 return;
987 if (className == QLatin1String("QRemoteObjectDynamicReplica") || staticTypes.contains(className))
988 return;
989 className.chop(7); //Remove 'Replica' from name
990 staticTypes.insert(className, metaObject);
991 }
992
connectReplica(QObject * object,QRemoteObjectReplica * instance)993 void QRemoteObjectNodePrivate::connectReplica(QObject *object, QRemoteObjectReplica *instance)
994 {
995 int nConnections = 0;
996 const QMetaObject *us = instance->metaObject();
997 const QMetaObject *them = object->metaObject();
998
999 static const int memberOffset = QRemoteObjectReplica::staticMetaObject.methodCount();
1000 for (int idx = memberOffset; idx < us->methodCount(); ++idx) {
1001 const QMetaMethod mm = us->method(idx);
1002
1003 qROPrivDebug() << idx << mm.name();
1004 if (mm.methodType() != QMetaMethod::Signal)
1005 continue;
1006
1007 // try to connect to a signal on the parent that has the same method signature
1008 QByteArray sig = QMetaObject::normalizedSignature(mm.methodSignature().constData());
1009 qROPrivDebug() << sig;
1010 if (them->indexOfSignal(sig.constData()) == -1)
1011 continue;
1012
1013 sig.prepend(QSIGNAL_CODE + '0');
1014 const char * const csig = sig.constData();
1015 const bool res = QObject::connect(object, csig, instance, csig);
1016 Q_UNUSED(res);
1017 ++nConnections;
1018
1019 qROPrivDebug() << sig << res;
1020 }
1021
1022 qROPrivDebug() << "# connections =" << nConnections;
1023 }
1024
openConnectionIfNeeded(const QString & name)1025 void QRemoteObjectNodePrivate::openConnectionIfNeeded(const QString &name)
1026 {
1027 qROPrivDebug() << Q_FUNC_INFO << name << this;
1028 if (!remoteObjectAddresses().contains(name)) {
1029 qROPrivDebug() << name << "not available - available addresses:" << remoteObjectAddresses();
1030 return;
1031 }
1032
1033 if (!initConnection(remoteObjectAddresses().value(name).hostUrl))
1034 qROPrivWarning() << "failed to open connection to" << name;
1035 }
1036
initConnection(const QUrl & address)1037 bool QRemoteObjectNodePrivate::initConnection(const QUrl &address)
1038 {
1039 Q_Q(QRemoteObjectNode);
1040 if (requestedUrls.contains(address)) {
1041 qROPrivDebug() << "Connection already requested for " << address.toString();
1042 return true;
1043 }
1044
1045 requestedUrls.insert(address);
1046
1047 if (schemaHandlers.contains(address.scheme())) {
1048 schemaHandlers[address.scheme()](address);
1049 return true;
1050 }
1051
1052 ClientIoDevice *connection = QtROClientFactory::instance()->create(address, q);
1053 if (!connection) {
1054 qROPrivWarning() << "Could not create ClientIoDevice for client. Invalid url/scheme provided?" << address;
1055 return false;
1056 }
1057 qROPrivDebug() << "Opening connection to" << address.toString();
1058 qROPrivDebug() << "Replica Connection isValid" << connection->isOpen();
1059 QObject::connect(connection, &ClientIoDevice::shouldReconnect, q, [this, connection]() {
1060 onShouldReconnect(connection);
1061 });
1062 QObject::connect(connection, &IoDeviceBase::readyRead, q, [this, connection]() {
1063 onClientRead(connection);
1064 });
1065 connection->connectToServer();
1066
1067 return true;
1068 }
1069
hasInstance(const QString & name)1070 bool QRemoteObjectNodePrivate::hasInstance(const QString &name)
1071 {
1072 if (!replicas.contains(name))
1073 return false;
1074
1075 QSharedPointer<QReplicaImplementationInterface> rep = replicas.value(name).toStrongRef();
1076 if (!rep) { //already deleted
1077 replicas.remove(name);
1078 return false;
1079 }
1080
1081 return true;
1082 }
1083
onRemoteObjectSourceAdded(const QRemoteObjectSourceLocation & entry)1084 void QRemoteObjectNodePrivate::onRemoteObjectSourceAdded(const QRemoteObjectSourceLocation &entry)
1085 {
1086 qROPrivDebug() << "onRemoteObjectSourceAdded" << entry << replicas << replicas.contains(entry.first);
1087 if (!entry.first.isEmpty()) {
1088 QRemoteObjectSourceLocations locs = registry->sourceLocations();
1089 locs[entry.first] = entry.second;
1090 //TODO Is there a way to extend QRemoteObjectSourceLocations in place?
1091 registry->d_impl->setProperty(0, QVariant::fromValue(locs));
1092 qROPrivDebug() << "onRemoteObjectSourceAdded, now locations =" << locs;
1093 }
1094 if (replicas.contains(entry.first)) //We have a replica waiting on this remoteObject
1095 {
1096 QSharedPointer<QReplicaImplementationInterface> rep = replicas.value(entry.first).toStrongRef();
1097 if (!rep) { //replica has been deleted, remove from list
1098 replicas.remove(entry.first);
1099 return;
1100 }
1101
1102 initConnection(entry.second.hostUrl);
1103
1104 qROPrivDebug() << "Called initConnection due to new RemoteObjectSource added via registry" << entry.first;
1105 }
1106 }
1107
onRemoteObjectSourceRemoved(const QRemoteObjectSourceLocation & entry)1108 void QRemoteObjectNodePrivate::onRemoteObjectSourceRemoved(const QRemoteObjectSourceLocation &entry)
1109 {
1110 if (!entry.first.isEmpty()) {
1111 QRemoteObjectSourceLocations locs = registry->sourceLocations();
1112 locs.remove(entry.first);
1113 registry->d_impl->setProperty(0, QVariant::fromValue(locs));
1114 }
1115 }
1116
onRegistryInitialized()1117 void QRemoteObjectNodePrivate::onRegistryInitialized()
1118 {
1119 qROPrivDebug() << "Registry Initialized" << remoteObjectAddresses();
1120
1121 const auto remotes = remoteObjectAddresses();
1122 for (auto i = remotes.cbegin(), end = remotes.cend(); i != end; ++i) {
1123 if (replicas.contains(i.key())) //We have a replica waiting on this remoteObject
1124 {
1125 QSharedPointer<QReplicaImplementationInterface> rep = replicas.value(i.key()).toStrongRef();
1126 if (rep && !requestedUrls.contains(i.value().hostUrl))
1127 initConnection(i.value().hostUrl);
1128 else if (!rep) //replica has been deleted, remove from list
1129 replicas.remove(i.key());
1130
1131 continue;
1132 }
1133 }
1134 }
1135
onShouldReconnect(ClientIoDevice * ioDevice)1136 void QRemoteObjectNodePrivate::onShouldReconnect(ClientIoDevice *ioDevice)
1137 {
1138 Q_Q(QRemoteObjectNode);
1139
1140 const auto remoteObjects = ioDevice->remoteObjects();
1141 for (const QString &remoteObject : remoteObjects) {
1142 connectedSources.remove(remoteObject);
1143 ioDevice->removeSource(remoteObject);
1144 if (replicas.contains(remoteObject)) { //We have a replica waiting on this remoteObject
1145 QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(remoteObject).toStrongRef());
1146 if (rep && !rep->connectionToSource.isNull()) {
1147 rep->setDisconnected();
1148 } else if (!rep) {
1149 replicas.remove(remoteObject);
1150 }
1151 }
1152 }
1153 if (requestedUrls.contains(ioDevice->url())) {
1154 // Only try to reconnect to URLs requested via connectToNode
1155 // If we connected via registry, wait for the registry to see the node/source again
1156 pendingReconnect.insert(ioDevice);
1157 if (!reconnectTimer.isActive()) {
1158 reconnectTimer.start(retryInterval, q);
1159 qROPrivDebug() << "Starting reconnect timer";
1160 }
1161 } else {
1162 qROPrivDebug() << "Url" << ioDevice->url().toDisplayString().toLatin1()
1163 << "lost. We will reconnect Replicas if they reappear on the Registry.";
1164 }
1165 }
1166
1167 //This version of handleNewAcquire creates a QConnectedReplica. If this is a
1168 //host node, the QRemoteObjectHostBasePrivate overload is called instead.
handleNewAcquire(const QMetaObject * meta,QRemoteObjectReplica * instance,const QString & name)1169 QReplicaImplementationInterface *QRemoteObjectNodePrivate::handleNewAcquire(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name)
1170 {
1171 Q_Q(QRemoteObjectNode);
1172 QConnectedReplicaImplementation *rp = new QConnectedReplicaImplementation(name, meta, q);
1173 rp->configurePrivate(instance);
1174 if (connectedSources.contains(name)) { //Either we have a peer connections, or existing connection via registry
1175 handleReplicaConnection(connectedSources[name].objectSignature, rp, connectedSources[name].device);
1176 } else {
1177 //No existing connection, but we know we can connect via registry
1178 const auto &sourceLocations = remoteObjectAddresses();
1179 const auto it = sourceLocations.constFind(name);
1180 // This will try the connection, and if successful, the remoteObjects will be sent
1181 // The link to the replica will be handled then
1182 if (it != sourceLocations.constEnd())
1183 initConnection(it.value().hostUrl);
1184 }
1185 return rp;
1186 }
1187
handleReplicaConnection(const QString & name)1188 void QRemoteObjectNodePrivate::handleReplicaConnection(const QString &name)
1189 {
1190 QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(name).toStrongRef());
1191 if (!rep) { //replica has been deleted, remove from list
1192 replicas.remove(name);
1193 return;
1194 }
1195
1196 if (rep->isShortCircuit())
1197 return;
1198
1199 QConnectedReplicaImplementation *connectedRep = static_cast<QConnectedReplicaImplementation *>(rep.data());
1200 if (connectedRep->connectionToSource.isNull()) {
1201 const auto sourceInfo = connectedSources.value(name);
1202 handleReplicaConnection(sourceInfo.objectSignature, connectedRep, sourceInfo.device);
1203 }
1204 }
1205
handleReplicaConnection(const QByteArray & sourceSignature,QConnectedReplicaImplementation * rep,IoDeviceBase * connection)1206 void QRemoteObjectNodePrivate::handleReplicaConnection(const QByteArray &sourceSignature, QConnectedReplicaImplementation *rep, IoDeviceBase *connection)
1207 {
1208 if (!checkSignatures(rep->m_objectSignature, sourceSignature)) {
1209 qROPrivWarning() << "Signature mismatch for" << rep->m_metaObject->className() << (rep->m_objectName.isEmpty() ? QLatin1String("(unnamed)") : rep->m_objectName);
1210 rep->setState(QRemoteObjectReplica::SignatureMismatch);
1211 return;
1212 }
1213 rep->setConnection(connection);
1214 }
1215
1216 //Host Nodes can use the more efficient InProcess Replica if we (this Node) hold the Source for the
1217 //requested Replica. If not, fall back to the Connected Replica case.
handleNewAcquire(const QMetaObject * meta,QRemoteObjectReplica * instance,const QString & name)1218 QReplicaImplementationInterface *QRemoteObjectHostBasePrivate::handleNewAcquire(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name)
1219 {
1220 QMap<QString, QRemoteObjectSourceBase*>::const_iterator mapIt;
1221 if (remoteObjectIo && map_contains(remoteObjectIo->m_sourceObjects, name, mapIt)) {
1222 Q_Q(QRemoteObjectHostBase);
1223 QInProcessReplicaImplementation *rp = new QInProcessReplicaImplementation(name, meta, q);
1224 rp->configurePrivate(instance);
1225 connectReplica(mapIt.value()->m_object, instance);
1226 rp->connectionToSource = mapIt.value();
1227 return rp;
1228 }
1229 return QRemoteObjectNodePrivate::handleNewAcquire(meta, instance, name);
1230 }
1231
onClientRead(QObject * obj)1232 void QRemoteObjectNodePrivate::onClientRead(QObject *obj)
1233 {
1234 using namespace QRemoteObjectPackets;
1235 IoDeviceBase *connection = qobject_cast<IoDeviceBase*>(obj);
1236 QRemoteObjectPacketTypeEnum packetType;
1237 Q_ASSERT(connection);
1238
1239 do {
1240 if (!connection->read(packetType, rxName))
1241 return;
1242
1243 if (packetType != Handshake && !m_handshakeReceived) {
1244 qROPrivWarning() << "Expected Handshake, got " << packetType;
1245 setLastError(QRemoteObjectNode::ProtocolMismatch);
1246 connection->close();
1247 break;
1248 }
1249
1250 switch (packetType) {
1251 case Pong:
1252 {
1253 QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef());
1254 if (rep)
1255 rep->notifyAboutReply(0, {});
1256 else //replica has been deleted, remove from list
1257 replicas.remove(rxName);
1258 break;
1259 }
1260 case Handshake:
1261 if (rxName != QtRemoteObjects::protocolVersion) {
1262 qWarning() << "*** Protocol Mismatch, closing connection ***. Got" << rxName << "expected" << QtRemoteObjects::protocolVersion;
1263 setLastError(QRemoteObjectNode::ProtocolMismatch);
1264 connection->close();
1265 } else {
1266 m_handshakeReceived = true;
1267 }
1268 break;
1269 case ObjectList:
1270 {
1271 deserializeObjectListPacket(connection->stream(), rxObjects);
1272 qROPrivDebug() << "newObjects:" << rxObjects;
1273 // We need to make sure all of the source objects are in connectedSources before we add connections,
1274 // otherwise nested QObjects could fail (we want to acquire children before parents, and the object
1275 // list is unordered)
1276 for (const auto &remoteObject : qAsConst(rxObjects)) {
1277 qROPrivDebug() << " connectedSources.contains(" << remoteObject << ")" << connectedSources.contains(remoteObject.name) << replicas.contains(remoteObject.name);
1278 if (!connectedSources.contains(remoteObject.name)) {
1279 connectedSources[remoteObject.name] = SourceInfo{connection, remoteObject.typeName, remoteObject.signature};
1280 connection->addSource(remoteObject.name);
1281 // Make sure we handle Registry first if it is available
1282 if (remoteObject.name == QLatin1String("Registry") && replicas.contains(remoteObject.name))
1283 handleReplicaConnection(remoteObject.name);
1284 }
1285 }
1286 for (const auto &remoteObject : qAsConst(rxObjects)) {
1287 if (replicas.contains(remoteObject.name)) //We have a replica waiting on this remoteObject
1288 handleReplicaConnection(remoteObject.name);
1289 }
1290 break;
1291 }
1292 case InitPacket:
1293 {
1294 qROPrivDebug() << "InitPacket-->" << rxName << this;
1295 QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(rxName).toStrongRef());
1296 //Use m_rxArgs (a QVariantList to hold the properties QVariantList)
1297 deserializeInitPacket(connection->stream(), rxArgs);
1298 if (rep)
1299 {
1300 handlePointerToQObjectProperties(rep.data(), rxArgs);
1301 rep->initialize(rxArgs);
1302 } else { //replica has been deleted, remove from list
1303 replicas.remove(rxName);
1304 }
1305 break;
1306 }
1307 case InitDynamicPacket:
1308 {
1309 qROPrivDebug() << "InitDynamicPacket-->" << rxName << this;
1310 const QMetaObject *meta = dynamicTypeManager.addDynamicType(connection, connection->stream());
1311 deserializeInitPacket(connection->stream(), rxArgs);
1312 QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(rxName).toStrongRef());
1313 if (rep)
1314 {
1315 rep->setDynamicMetaObject(meta);
1316 handlePointerToQObjectProperties(rep.data(), rxArgs);
1317 rep->setDynamicProperties(rxArgs);
1318 } else { //replica has been deleted, remove from list
1319 replicas.remove(rxName);
1320 }
1321 break;
1322 }
1323 case RemoveObject:
1324 {
1325 qROPrivDebug() << "RemoveObject-->" << rxName << this;
1326 connectedSources.remove(rxName);
1327 connection->removeSource(rxName);
1328 if (replicas.contains(rxName)) { //We have a replica using the removed source
1329 QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(rxName).toStrongRef());
1330 if (rep && !rep->connectionToSource.isNull()) {
1331 rep->connectionToSource.clear();
1332 rep->setState(QRemoteObjectReplica::Suspect);
1333 } else if (!rep) {
1334 replicas.remove(rxName);
1335 }
1336 }
1337 break;
1338 }
1339 case PropertyChangePacket:
1340 {
1341 int propertyIndex;
1342 deserializePropertyChangePacket(connection->stream(), propertyIndex, rxValue);
1343 QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef());
1344 if (rep) {
1345 QConnectedReplicaImplementation *connectedRep = nullptr;
1346 if (!rep->isShortCircuit()) {
1347 connectedRep = static_cast<QConnectedReplicaImplementation *>(rep.data());
1348 if (!connectedRep->childIndices().contains(propertyIndex))
1349 connectedRep = nullptr; //connectedRep will be a valid pointer only if propertyIndex is a child index
1350 }
1351 if (connectedRep)
1352 rep->setProperty(propertyIndex, handlePointerToQObjectProperty(connectedRep, propertyIndex, rxValue));
1353 else {
1354 const QMetaProperty property = rep->m_metaObject->property(propertyIndex + rep->m_metaObject->propertyOffset());
1355 if (property.userType() == QMetaType::QVariant && rxValue.canConvert<QRO_>()) {
1356 // This is a type that requires registration
1357 QRO_ typeInfo = rxValue.value<QRO_>();
1358 QDataStream in(typeInfo.classDefinition);
1359 parseGadgets(connection, in);
1360 QDataStream ds(typeInfo.parameters);
1361 ds >> rxValue;
1362 }
1363 rep->setProperty(propertyIndex, decodeVariant(rxValue, property.userType()));
1364 }
1365 } else { //replica has been deleted, remove from list
1366 replicas.remove(rxName);
1367 }
1368 break;
1369 }
1370 case InvokePacket:
1371 {
1372 int call, index, serialId, propertyIndex;
1373 deserializeInvokePacket(connection->stream(), call, index, rxArgs, serialId, propertyIndex);
1374 QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef());
1375 if (rep) {
1376 static QVariant null(QMetaType::QObjectStar, (void*)0);
1377 QVariant paramValue;
1378 // Qt usually supports 9 arguments, so ten should be usually safe
1379 QVarLengthArray<void*, 10> param(rxArgs.size() + 1);
1380 param[0] = null.data(); //Never a return value
1381 if (rxArgs.size()) {
1382 auto signal = rep->m_metaObject->method(index+rep->m_signalOffset);
1383 for (int i = 0; i < rxArgs.size(); i++) {
1384 if (signal.parameterType(i) == QMetaType::QVariant)
1385 param[i + 1] = const_cast<void*>(reinterpret_cast<const void*>(&rxArgs.at(i)));
1386 else {
1387 decodeVariant(rxArgs[i], signal.parameterType(i));
1388 param[i + 1] = const_cast<void *>(rxArgs.at(i).data());
1389 }
1390 }
1391 } else if (propertyIndex != -1) {
1392 param.resize(2);
1393 paramValue = rep->getProperty(propertyIndex);
1394 param[1] = paramValue.data();
1395 }
1396 qROPrivDebug() << "Replica Invoke-->" << rxName << rep->m_metaObject->method(index+rep->m_signalOffset).name() << index << rep->m_signalOffset;
1397 // We activate on rep->metaobject() so the private metacall is used, not m_metaobject (which
1398 // is the class thie replica looks like)
1399 QMetaObject::activate(rep.data(), rep->metaObject(), index+rep->m_signalOffset, param.data());
1400 } else { //replica has been deleted, remove from list
1401 replicas.remove(rxName);
1402 }
1403 break;
1404 }
1405 case InvokeReplyPacket:
1406 {
1407 int ackedSerialId;
1408 deserializeInvokeReplyPacket(connection->stream(), ackedSerialId, rxValue);
1409 QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef());
1410 if (rep) {
1411 qROPrivDebug() << "Received InvokeReplyPacket ack'ing serial id:" << ackedSerialId;
1412 rep->notifyAboutReply(ackedSerialId, rxValue);
1413 } else { //replica has been deleted, remove from list
1414 replicas.remove(rxName);
1415 }
1416 break;
1417 }
1418 case AddObject:
1419 case Invalid:
1420 case Ping:
1421 qROPrivWarning() << "Unexpected packet received";
1422 }
1423 } while (connection->bytesAvailable()); // have bytes left over, so do another iteration
1424 }
1425
1426 /*!
1427 \class QRemoteObjectNode
1428 \inmodule QtRemoteObjects
1429 \brief A node on a Qt Remote Objects network.
1430
1431 The QRemoteObjectNode class provides an entry point to a QtRemoteObjects
1432 network. A network can be as simple as two nodes, or an arbitrarily complex
1433 set of processes and devices.
1434
1435 A QRemoteObjectNode does not have a url that other nodes can connect to,
1436 and thus is able to acquire replicas only. It is not able to share source
1437 objects (only QRemoteObjectHost and QRemoteObjectRegistryHost Nodes can
1438 share).
1439
1440 Nodes may connect to each other directly using \l connectToNode, or
1441 they can use the QRemoteObjectRegistry to simplify connections.
1442
1443 The QRemoteObjectRegistry is a special replica available to every node that
1444 connects to the Registry Url. It knows how to connect to every
1445 QRemoteObjectSource object on the network.
1446
1447 \sa QRemoteObjectHost, QRemoteObjectRegistryHost
1448 */
1449
1450 /*!
1451 \class QRemoteObjectHostBase
1452 \inmodule QtRemoteObjects
1453 \brief The QRemoteObjectHostBase class provides base functionality common to \l
1454 {QRemoteObjectHost} {Host} and \l {QRemoteObjectRegistryHost} {RegistryHost} classes.
1455
1456 QRemoteObjectHostBase is a base class that cannot be instantiated directly.
1457 It provides the enableRemoting and disableRemoting functionality shared by
1458 all host nodes (\l {QRemoteObjectHost} {Host} and \l
1459 {QRemoteObjectRegistryHost} {RegistryHost}) as well as the logic required
1460 to expose \l {Source} objects on the Remote Objects network.
1461 */
1462
1463 /*!
1464 \class QRemoteObjectHost
1465 \inmodule QtRemoteObjects
1466 \brief A (Host) Node on a Qt Remote Objects network.
1467
1468 The QRemoteObjectHost class provides an entry point to a QtRemoteObjects
1469 network. A network can be as simple as two nodes, or an arbitrarily complex
1470 set of processes and devices.
1471
1472 QRemoteObjectHosts have the same capabilities as QRemoteObjectNodes, but
1473 they can also be connected to and can share source objects on the network.
1474
1475 Nodes may connect to each other directly using \l connectToNode, or they
1476 can use the QRemoteObjectRegistry to simplify connections.
1477
1478 The QRemoteObjectRegistry is a special replica available to every node that
1479 connects to the uegistry Url. It knows how to connect to every
1480 QRemoteObjectSource object on the network.
1481
1482 \sa QRemoteObjectNode, QRemoteObjectRegistryHost
1483 */
1484
1485 /*!
1486 \class QRemoteObjectRegistryHost
1487 \inmodule QtRemoteObjects
1488 \brief A (Host/Registry) node on a Qt Remote Objects network.
1489
1490 The QRemoteObjectRegistryHost class provides an entry point to a QtRemoteObjects
1491 network. A network can be as simple as two Nodes, or an arbitrarily complex
1492 set of processes and devices.
1493
1494 A QRemoteObjectRegistryHost has the same capability that a
1495 QRemoteObjectHost has (which includes everything a QRemoteObjectNode
1496 supports), and in addition is the owner of the Registry. Any
1497 QRemoteObjectHost node that connects to this Node will have all of their
1498 Source objects made available by the Registry.
1499
1500 Nodes only support connection to one \l registry, calling \l
1501 QRemoteObjectNode::setRegistryUrl when a Registry is already set is
1502 considered an error. For something like a secure and insecure network
1503 (where different Registries would be applicable), the recommendation is to
1504 create separate Nodes to connect to each, in effect creating two
1505 independent Qt Remote Objects networks.
1506
1507 Nodes may connect to each other directly using \l connectToNode, or they
1508 can use the QRemoteObjectRegistry to simplify connections.
1509
1510 The QRemoteObjectRegistry is a special Replica available to every Node that
1511 connects to the Registry Url. It knows how to connect to every
1512 QRemoteObjectSource object on the network.
1513
1514 \sa QRemoteObjectNode, QRemoteObjectHost
1515 */
1516
1517 /*!
1518 \enum QRemoteObjectNode::ErrorCode
1519
1520 This enum type specifies the various error codes associated with QRemoteObjectNode errors:
1521
1522 \value NoError No error.
1523 \value RegistryNotAcquired The registry could not be acquired.
1524 \value RegistryAlreadyHosted The registry is already defined and hosting Sources.
1525 \value NodeIsNoServer The given QRemoteObjectNode is not a host node.
1526 \value ServerAlreadyCreated The host node has already been initialized.
1527 \value UnintendedRegistryHosting An attempt was made to create a host QRemoteObjectNode and connect to itself as the registry.
1528 \value OperationNotValidOnClientNode The attempted operation is not valid on a client QRemoteObjectNode.
1529 \value SourceNotRegistered The given QRemoteObjectSource is not registered on this node.
1530 \value MissingObjectName The given QObject does not have objectName() set.
1531 \value HostUrlInvalid The given url has an invalid or unrecognized scheme.
1532 \value ProtocolMismatch The client and the server have different protocol versions.
1533 \value ListenFailed Can't listen on the specified host port.
1534 */
1535
1536 /*!
1537 \enum QRemoteObjectHostBase::AllowedSchemas
1538
1539 This enum is used to specify whether a Node will accept a url with an
1540 unrecognized schema for the hostUrl. By default only urls with known
1541 schemas are accepted, but using \c AllowExternalRegistration will enable
1542 the \l Registry to pass your external (to QtRO) url to client Nodes.
1543
1544 \value BuiltInSchemasOnly Only allow the hostUrl to be set to a QtRO
1545 supported schema. This is the default value, and causes a Node error to be
1546 set if an unrecognized schema is provided.
1547 \value AllowExternalRegistration The provided schema is registered as an
1548 \l {External Schemas}{External Schema}
1549
1550 \sa QRemoteObjectHost
1551 */
1552
1553 /*!
1554 \fn ObjectType *QRemoteObjectNode::acquire(const QString &name)
1555
1556 Returns a pointer to a Replica of type ObjectType (which is a template
1557 parameter and must inherit from \l QRemoteObjectReplica). That is, the
1558 template parameter must be a \l {repc} generated type. The \a name
1559 parameter can be used to specify the \a name given to the object
1560 during the QRemoteObjectHost::enableRemoting() call.
1561 */
1562
initialize()1563 void QRemoteObjectNodePrivate::initialize()
1564 {
1565 qRegisterMetaType<QRemoteObjectNode *>();
1566 qRegisterMetaType<QRemoteObjectNode::ErrorCode>();
1567 qRegisterMetaType<QAbstractSocket::SocketError>(); //For queued qnx error()
1568 qRegisterMetaTypeStreamOperators<QVector<int> >();
1569 qRegisterMetaTypeStreamOperators<QRemoteObjectPackets::QRO_>();
1570 // To support dynamic MODELs, we need to make sure the types are registered
1571 QAbstractItemModelSourceAdapter::registerTypes();
1572 }
1573
checkSignatures(const QByteArray & a,const QByteArray & b)1574 bool QRemoteObjectNodePrivate::checkSignatures(const QByteArray &a, const QByteArray &b)
1575 {
1576 // if any of a or b is empty it means it's a dynamic ojects or an item model
1577 if (a.isEmpty() || b.isEmpty())
1578 return true;
1579 return a == b;
1580 }
1581
1582
persistProperties(const QString & repName,const QByteArray & repSig,const QVariantList & props)1583 void QRemoteObjectNode::persistProperties(const QString &repName, const QByteArray &repSig, const QVariantList &props)
1584 {
1585 Q_D(QRemoteObjectNode);
1586 if (d->persistedStore) {
1587 d->persistedStore->saveProperties(repName, repSig, props);
1588 } else {
1589 qCWarning(QT_REMOTEOBJECT) << qPrintable(objectName()) << "Unable to store persisted properties for" << repName;
1590 qCWarning(QT_REMOTEOBJECT) << " No persisted store set.";
1591 }
1592 }
1593
retrieveProperties(const QString & repName,const QByteArray & repSig)1594 QVariantList QRemoteObjectNode::retrieveProperties(const QString &repName, const QByteArray &repSig)
1595 {
1596 Q_D(QRemoteObjectNode);
1597 if (d->persistedStore) {
1598 return d->persistedStore->restoreProperties(repName, repSig);
1599 }
1600 qCWarning(QT_REMOTEOBJECT) << qPrintable(objectName()) << "Unable to retrieve persisted properties for" << repName;
1601 qCWarning(QT_REMOTEOBJECT) << " No persisted store set.";
1602 return QVariantList();
1603 }
1604
1605 /*!
1606 Default constructor for QRemoteObjectNode with the given \a parent. A Node
1607 constructed in this manner can not be connected to, and thus can not expose
1608 Source objects on the network. It also will not include a \l
1609 QRemoteObjectRegistry, unless set manually using setRegistryUrl.
1610
1611 \sa connectToNode, setRegistryUrl
1612 */
QRemoteObjectNode(QObject * parent)1613 QRemoteObjectNode::QRemoteObjectNode(QObject *parent)
1614 : QObject(*new QRemoteObjectNodePrivate, parent)
1615 {
1616 Q_D(QRemoteObjectNode);
1617 d->initialize();
1618 }
1619
1620 /*!
1621 QRemoteObjectNode connected to a {QRemoteObjectRegistry} {Registry}. A Node
1622 constructed in this manner can not be connected to, and thus can not expose
1623 Source objects on the network. Finding and connecting to other (Host) Nodes
1624 is handled by the QRemoteObjectRegistry specified by \a registryAddress.
1625
1626 \sa connectToNode, setRegistryUrl, QRemoteObjectHost, QRemoteObjectRegistryHost
1627 */
QRemoteObjectNode(const QUrl & registryAddress,QObject * parent)1628 QRemoteObjectNode::QRemoteObjectNode(const QUrl ®istryAddress, QObject *parent)
1629 : QObject(*new QRemoteObjectNodePrivate, parent)
1630 {
1631 Q_D(QRemoteObjectNode);
1632 d->initialize();
1633 setRegistryUrl(registryAddress);
1634 }
1635
QRemoteObjectNode(QRemoteObjectNodePrivate & dptr,QObject * parent)1636 QRemoteObjectNode::QRemoteObjectNode(QRemoteObjectNodePrivate &dptr, QObject *parent)
1637 : QObject(dptr, parent)
1638 {
1639 Q_D(QRemoteObjectNode);
1640 d->initialize();
1641 }
1642
1643 /*!
1644 \qmltype Host
1645 \instantiates QRemoteObjectHost
1646 \inqmlmodule QtRemoteObjects
1647 \brief A host node on a Qt Remote Objects network.
1648
1649 The Host type provides an entry point to a Qt Remote Objects network. A network
1650 can be as simple as two nodes, or an arbitrarily complex set of processes and devices.
1651
1652 Hosts have the same capabilities as Nodes, but they can also be connected to and can
1653 share source objects on the network.
1654 */
1655
1656 /*!
1657 \internal This is a base class for both QRemoteObjectHost and
1658 QRemoteObjectRegistryHost to provide the shared features/functions for
1659 sharing \l Source objects.
1660 */
QRemoteObjectHostBase(QRemoteObjectHostBasePrivate & d,QObject * parent)1661 QRemoteObjectHostBase::QRemoteObjectHostBase(QRemoteObjectHostBasePrivate &d, QObject *parent)
1662 : QRemoteObjectNode(d, parent)
1663 { }
1664
1665 /*!
1666 Constructs a new QRemoteObjectHost Node (i.e., a Node that supports
1667 exposing \l Source objects on the QtRO network) with the given \a parent.
1668 This constructor is meant specific to support QML in the future as it will
1669 not be available to connect to until \l {QRemoteObjectHost::}{setHostUrl} is called.
1670
1671 \sa setHostUrl(), setRegistryUrl()
1672 */
QRemoteObjectHost(QObject * parent)1673 QRemoteObjectHost::QRemoteObjectHost(QObject *parent)
1674 : QRemoteObjectHostBase(*new QRemoteObjectHostPrivate, parent)
1675 { }
1676
1677 /*!
1678 Constructs a new QRemoteObjectHost Node (i.e., a Node that supports
1679 exposing \l Source objects on the QtRO network) with address \a address. If
1680 set, \a registryAddress will be used to connect to the \l
1681 QRemoteObjectRegistry at the provided address. The \a allowedSchemas
1682 parameter is only needed (and should be set to \l
1683 {QRemoteObjectHostBase::AllowExternalRegistration}
1684 {AllowExternalRegistration}) if the schema of the url should be used as an
1685 \l {External Schemas} {External Schema} by the registry.
1686
1687 \sa setHostUrl(), setRegistryUrl()
1688 */
QRemoteObjectHost(const QUrl & address,const QUrl & registryAddress,AllowedSchemas allowedSchemas,QObject * parent)1689 QRemoteObjectHost::QRemoteObjectHost(const QUrl &address, const QUrl ®istryAddress,
1690 AllowedSchemas allowedSchemas, QObject *parent)
1691 : QRemoteObjectHostBase(*new QRemoteObjectHostPrivate, parent)
1692 {
1693 if (!address.isEmpty()) {
1694 if (!setHostUrl(address, allowedSchemas))
1695 return;
1696 }
1697
1698 if (!registryAddress.isEmpty())
1699 setRegistryUrl(registryAddress);
1700 }
1701
1702 /*!
1703 Constructs a new QRemoteObjectHost Node (i.e., a Node that supports
1704 exposing \l Source objects on the QtRO network) with a url of \a
1705 address and the given \a parent. This overload is provided as a convenience for specifying a
1706 QObject parent without providing a registry address.
1707
1708 \sa setHostUrl(), setRegistryUrl()
1709 */
QRemoteObjectHost(const QUrl & address,QObject * parent)1710 QRemoteObjectHost::QRemoteObjectHost(const QUrl &address, QObject *parent)
1711 : QRemoteObjectHostBase(*new QRemoteObjectHostPrivate, parent)
1712 {
1713 if (!address.isEmpty())
1714 setHostUrl(address);
1715 }
1716
1717 /*!
1718 \internal QRemoteObjectHost::QRemoteObjectHost
1719 */
QRemoteObjectHost(QRemoteObjectHostPrivate & d,QObject * parent)1720 QRemoteObjectHost::QRemoteObjectHost(QRemoteObjectHostPrivate &d, QObject *parent)
1721 : QRemoteObjectHostBase(d, parent)
1722 { }
1723
~QRemoteObjectHost()1724 QRemoteObjectHost::~QRemoteObjectHost() {}
1725
1726 /*!
1727 Constructs a new QRemoteObjectRegistryHost Node with the given \a parent. RegistryHost Nodes have
1728 the same functionality as \l QRemoteObjectHost Nodes, except rather than
1729 being able to connect to a \l QRemoteObjectRegistry, the provided Host QUrl
1730 (\a registryAddress) becomes the address of the registry for other Nodes to
1731 connect to.
1732 */
QRemoteObjectRegistryHost(const QUrl & registryAddress,QObject * parent)1733 QRemoteObjectRegistryHost::QRemoteObjectRegistryHost(const QUrl ®istryAddress, QObject *parent)
1734 : QRemoteObjectHostBase(*new QRemoteObjectRegistryHostPrivate, parent)
1735 {
1736 if (registryAddress.isEmpty())
1737 return;
1738
1739 setRegistryUrl(registryAddress);
1740 }
1741
1742 /*!
1743 \internal
1744 */
QRemoteObjectRegistryHost(QRemoteObjectRegistryHostPrivate & d,QObject * parent)1745 QRemoteObjectRegistryHost::QRemoteObjectRegistryHost(QRemoteObjectRegistryHostPrivate &d, QObject *parent)
1746 : QRemoteObjectHostBase(d, parent)
1747 { }
1748
~QRemoteObjectRegistryHost()1749 QRemoteObjectRegistryHost::~QRemoteObjectRegistryHost() {}
1750
~QRemoteObjectNode()1751 QRemoteObjectNode::~QRemoteObjectNode()
1752 { }
1753
~QRemoteObjectHostBase()1754 QRemoteObjectHostBase::~QRemoteObjectHostBase()
1755 { }
1756
1757 /*!
1758 Sets \a name as the internal name for this Node. This
1759 is then output as part of the logging (if enabled).
1760 This is primarily useful if you merge log data from multiple nodes.
1761 */
setName(const QString & name)1762 void QRemoteObjectNode::setName(const QString &name)
1763 {
1764 setObjectName(name);
1765 }
1766
1767 /*!
1768 Similar to QObject::setObjectName() (which this method calls), but this
1769 version also applies the \a name to internal classes as well, which are
1770 used in some of the debugging output.
1771 */
setName(const QString & name)1772 void QRemoteObjectHostBase::setName(const QString &name)
1773 {
1774 Q_D(QRemoteObjectHostBase);
1775 setObjectName(name);
1776 if (d->remoteObjectIo)
1777 d->remoteObjectIo->setObjectName(name);
1778 }
1779
1780 /*!
1781 \internal The HostBase version of this method is protected so the method
1782 isn't exposed on RegistryHost nodes.
1783 */
hostUrl() const1784 QUrl QRemoteObjectHostBase::hostUrl() const
1785 {
1786 Q_D(const QRemoteObjectHostBase);
1787 return d->remoteObjectIo->serverAddress();
1788 }
1789
1790 /*!
1791 \internal The HostBase version of this method is protected so the method
1792 isn't exposed on RegistryHost nodes.
1793 */
setHostUrl(const QUrl & hostAddress,AllowedSchemas allowedSchemas)1794 bool QRemoteObjectHostBase::setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas)
1795 {
1796 Q_D(QRemoteObjectHostBase);
1797 if (d->remoteObjectIo) {
1798 d->setLastError(ServerAlreadyCreated);
1799 return false;
1800 }
1801
1802 if (allowedSchemas == AllowedSchemas::BuiltInSchemasOnly && !QtROServerFactory::instance()->isValid(hostAddress)) {
1803 d->setLastError(HostUrlInvalid);
1804 return false;
1805 }
1806
1807 if (allowedSchemas == AllowedSchemas::AllowExternalRegistration && QtROServerFactory::instance()->isValid(hostAddress)) {
1808 qWarning() << qPrintable(objectName()) << "Overriding a valid QtRO url (" << hostAddress << ") with AllowExternalRegistration is not allowed.";
1809 d->setLastError(HostUrlInvalid);
1810 return false;
1811 }
1812 d->remoteObjectIo = new QRemoteObjectSourceIo(hostAddress, this);
1813
1814 if (allowedSchemas == AllowedSchemas::BuiltInSchemasOnly && !d->remoteObjectIo->startListening()) {
1815 d->setLastError(ListenFailed);
1816 delete d->remoteObjectIo;
1817 d->remoteObjectIo = nullptr;
1818 return false;
1819 }
1820
1821 //If we've given a name to the node, set it on the sourceIo as well
1822 if (!objectName().isEmpty())
1823 d->remoteObjectIo->setObjectName(objectName());
1824 //Since we don't know whether setHostUrl or setRegistryUrl/setRegistryHost will be called first,
1825 //break it into two pieces. setHostUrl connects the RemoteObjectSourceIo->[add/remove]RemoteObjectSource to QRemoteObjectReplicaNode->[add/remove]RemoteObjectSource
1826 //setRegistry* calls appropriately connect RemoteObjecSourcetIo->[add/remove]RemoteObjectSource to the registry when it is created
1827 QObject::connect(d->remoteObjectIo, &QRemoteObjectSourceIo::remoteObjectAdded, this, &QRemoteObjectHostBase::remoteObjectAdded);
1828 QObject::connect(d->remoteObjectIo, &QRemoteObjectSourceIo::remoteObjectRemoved, this, &QRemoteObjectHostBase::remoteObjectRemoved);
1829
1830 return true;
1831 }
1832
1833 /*!
1834 \qmlproperty url Host::hostUrl
1835
1836 The host address for the node.
1837
1838 This is the address where source objects remoted by this node will reside.
1839 */
1840
1841 /*!
1842 \property QRemoteObjectHost::hostUrl
1843 \brief The host address for the node.
1844
1845 This is the address where source objects remoted by this node will reside.
1846 */
1847
1848 /*!
1849 Returns the host address for the QRemoteObjectNode as a QUrl. If the Node
1850 is not a Host node, returns an empty QUrl.
1851
1852 \sa setHostUrl()
1853 */
hostUrl() const1854 QUrl QRemoteObjectHost::hostUrl() const
1855 {
1856 return QRemoteObjectHostBase::hostUrl();
1857 }
1858
1859 /*!
1860 Sets the \a hostAddress for a host QRemoteObjectNode.
1861
1862 Returns \c true if the Host address is set, otherwise \c false.
1863
1864 The \a allowedSchemas parameter is only needed (and should be set to \l
1865 {QRemoteObjectHostBase::AllowExternalRegistration}
1866 {AllowExternalRegistration}) if the schema of the url should be used as an
1867 \l {External Schemas} {External Schema} by the registry.
1868 */
setHostUrl(const QUrl & hostAddress,AllowedSchemas allowedSchemas)1869 bool QRemoteObjectHost::setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas)
1870 {
1871 bool success = QRemoteObjectHostBase::setHostUrl(hostAddress, allowedSchemas);
1872 if (success)
1873 emit hostUrlChanged();
1874 return success;
1875 }
1876
1877 /*!
1878 This method can be used to set the address of this Node to \a registryUrl
1879 (used for other Nodes to connect to this one), if the QUrl isn't set in the
1880 constructor. Since this Node becomes the Registry, calling this setter
1881 method causes this Node to use the url as the host address. All other
1882 Node's use the \l {QRemoteObjectNode::setRegistryUrl} method initiate a
1883 connection to the Registry.
1884
1885 Returns \c true if the registry address is set, otherwise \c false.
1886
1887 \sa QRemoteObjectRegistryHost(), QRemoteObjectNode::setRegistryUrl
1888 */
setRegistryUrl(const QUrl & registryUrl)1889 bool QRemoteObjectRegistryHost::setRegistryUrl(const QUrl ®istryUrl)
1890 {
1891 Q_D(QRemoteObjectRegistryHost);
1892 if (setHostUrl(registryUrl)) {
1893 if (!d->remoteObjectIo) {
1894 d->setLastError(ServerAlreadyCreated);
1895 return false;
1896 } else if (d->registry) {
1897 d->setLastError(RegistryAlreadyHosted);
1898 return false;
1899 }
1900
1901 QRegistrySource *remoteObject = new QRegistrySource(this);
1902 enableRemoting(remoteObject);
1903 d->registryAddress = d->remoteObjectIo->serverAddress();
1904 d->registrySource = remoteObject;
1905 //Connect RemoteObjectSourceIo->remoteObject[Added/Removde] to the registry Slot
1906 QObject::connect(this, &QRemoteObjectRegistryHost::remoteObjectAdded, d->registrySource, &QRegistrySource::addSource);
1907 QObject::connect(this, &QRemoteObjectRegistryHost::remoteObjectRemoved, d->registrySource, &QRegistrySource::removeSource);
1908 QObject::connect(d->remoteObjectIo, &QRemoteObjectSourceIo::serverRemoved,d->registrySource, &QRegistrySource::removeServer);
1909 //onAdd/Remove update the known remoteObjects list in the RegistrySource, so no need to connect to the RegistrySource remoteObjectAdded/Removed signals
1910 d->setRegistry(acquire<QRemoteObjectRegistry>());
1911 return true;
1912 }
1913 return false;
1914 }
1915
1916 /*!
1917 Returns the last error set.
1918 */
lastError() const1919 QRemoteObjectNode::ErrorCode QRemoteObjectNode::lastError() const
1920 {
1921 Q_D(const QRemoteObjectNode);
1922 return d->lastError;
1923 }
1924
1925 /*!
1926 \qmlproperty url Node::registryUrl
1927
1928 The address of the \l {QRemoteObjectRegistry} {Registry} used by this node.
1929
1930 This is an empty QUrl if there is no registry in use.
1931 */
1932
1933 /*!
1934 \property QRemoteObjectNode::registryUrl
1935 \brief The address of the \l {QRemoteObjectRegistry} {Registry} used by this node.
1936
1937 This is an empty QUrl if there is no registry in use.
1938 */
registryUrl() const1939 QUrl QRemoteObjectNode::registryUrl() const
1940 {
1941 Q_D(const QRemoteObjectNode);
1942 return d->registryAddress;
1943 }
1944
setRegistryUrl(const QUrl & registryAddress)1945 bool QRemoteObjectNode::setRegistryUrl(const QUrl ®istryAddress)
1946 {
1947 Q_D(QRemoteObjectNode);
1948 if (d->registry) {
1949 d->setLastError(RegistryAlreadyHosted);
1950 return false;
1951 }
1952
1953 d->registryAddress = registryAddress;
1954 d->setRegistry(acquire<QRemoteObjectRegistry>());
1955 //Connect remoteObject[Added/Removed] to the registry Slot
1956 QObject::connect(this, &QRemoteObjectNode::remoteObjectAdded, d->registry, &QRemoteObjectRegistry::addSource);
1957 QObject::connect(this, &QRemoteObjectNode::remoteObjectRemoved, d->registry, &QRemoteObjectRegistry::removeSource);
1958 connectToNode(registryAddress);
1959 return true;
1960 }
1961
setRegistry(QRemoteObjectRegistry * reg)1962 void QRemoteObjectNodePrivate::setRegistry(QRemoteObjectRegistry *reg)
1963 {
1964 Q_Q(QRemoteObjectNode);
1965 registry = reg;
1966 reg->setParent(q);
1967 //Make sure when we get the registry initialized, we update our replicas
1968 QObject::connect(reg, &QRemoteObjectRegistry::initialized, q, [this]() {
1969 onRegistryInitialized();
1970 });
1971 //Make sure we handle new RemoteObjectSources on Registry...
1972 QObject::connect(reg, &QRemoteObjectRegistry::remoteObjectAdded,
1973 q, [this](const QRemoteObjectSourceLocation &location) {
1974 onRemoteObjectSourceAdded(location);
1975 });
1976 QObject::connect(reg, &QRemoteObjectRegistry::remoteObjectRemoved,
1977 q, [this](const QRemoteObjectSourceLocation &location) {
1978 onRemoteObjectSourceRemoved(location);
1979 });
1980 }
1981
handlePointerToQObjectProperty(QConnectedReplicaImplementation * rep,int index,const QVariant & property)1982 QVariant QRemoteObjectNodePrivate::handlePointerToQObjectProperty(QConnectedReplicaImplementation *rep, int index, const QVariant &property)
1983 {
1984 Q_Q(QRemoteObjectNode);
1985 using namespace QRemoteObjectPackets;
1986
1987 QVariant retval;
1988
1989 Q_ASSERT(property.canConvert<QRO_>());
1990 QRO_ childInfo = property.value<QRO_>();
1991 qROPrivDebug() << "QRO_:" << childInfo.name << replicas.contains(childInfo.name) << replicas.keys();
1992 if (childInfo.isNull) {
1993 // Either the source has changed the pointer and we need to update it, or the source pointer is a nullptr
1994 if (replicas.contains(childInfo.name))
1995 replicas.remove(childInfo.name);
1996 if (childInfo.type == ObjectType::CLASS)
1997 retval = QVariant::fromValue<QRemoteObjectDynamicReplica*>(nullptr);
1998 else
1999 retval = QVariant::fromValue<QAbstractItemModelReplica*>(nullptr);
2000 return retval;
2001 }
2002
2003 const bool newReplica = !replicas.contains(childInfo.name) || rep->isInitialized();
2004 if (newReplica) {
2005 if (rep->isInitialized()) {
2006 auto childRep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.take(childInfo.name));
2007 if (childRep && !childRep->isShortCircuit()) {
2008 qCDebug(QT_REMOTEOBJECT) << "Checking if dynamic type should be added to dynamicTypeManager (type =" << childRep->m_metaObject->className() << ")";
2009 dynamicTypeManager.addFromMetaObject(childRep->m_metaObject);
2010 }
2011 }
2012 if (childInfo.type == ObjectType::CLASS)
2013 retval = QVariant::fromValue(q->acquireDynamic(childInfo.name));
2014 else
2015 retval = QVariant::fromValue(q->acquireModel(childInfo.name));
2016 } else //We are receiving the initial data for the QObject
2017 retval = rep->getProperty(index); //Use existing value so changed signal isn't emitted
2018
2019 QSharedPointer<QConnectedReplicaImplementation> childRep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(childInfo.name).toStrongRef());
2020 if (childRep->connectionToSource.isNull())
2021 childRep->connectionToSource = rep->connectionToSource;
2022 QVariantList parameters;
2023 QDataStream ds(childInfo.parameters);
2024 if (childRep->needsDynamicInitialization()) {
2025 if (childInfo.classDefinition.isEmpty()) {
2026 auto typeName = childInfo.typeName;
2027 if (typeName == QLatin1String("QObject")) {
2028 // The sender would have included the class name if needed
2029 // So the acquire must have been templated, and we have the typeName
2030 typeName = QString::fromLatin1(rep->getProperty(index).typeName());
2031 if (typeName.endsWith(QLatin1String("Replica*")))
2032 typeName.chop(8);
2033 }
2034 childRep->setDynamicMetaObject(dynamicTypeManager.metaObjectForType(typeName));
2035 } else {
2036 QDataStream in(childInfo.classDefinition);
2037 childRep->setDynamicMetaObject(dynamicTypeManager.addDynamicType(rep->connectionToSource, in));
2038 }
2039 if (!childInfo.parameters.isEmpty())
2040 ds >> parameters;
2041 handlePointerToQObjectProperties(childRep.data(), parameters);
2042 childRep->setDynamicProperties(parameters);
2043 } else {
2044 if (!childInfo.parameters.isEmpty())
2045 ds >> parameters;
2046 handlePointerToQObjectProperties(childRep.data(), parameters);
2047 childRep->initialize(parameters);
2048 }
2049
2050 return retval;
2051 }
2052
handlePointerToQObjectProperties(QConnectedReplicaImplementation * rep,QVariantList & properties)2053 void QRemoteObjectNodePrivate::handlePointerToQObjectProperties(QConnectedReplicaImplementation *rep, QVariantList &properties)
2054 {
2055 for (const int index : rep->childIndices())
2056 properties[index] = handlePointerToQObjectProperty(rep, index, properties.at(index));
2057 }
2058
2059 /*!
2060 Blocks until this Node's \l Registry is initialized or \a timeout (in
2061 milliseconds) expires. Returns \c true if the \l Registry is successfully
2062 initialized upon return, or \c false otherwise.
2063 */
waitForRegistry(int timeout)2064 bool QRemoteObjectNode::waitForRegistry(int timeout)
2065 {
2066 Q_D(QRemoteObjectNode);
2067 if (!d->registry) {
2068 qCWarning(QT_REMOTEOBJECT) << qPrintable(objectName()) << "waitForRegistry() error: No valid registry url set";
2069 return false;
2070 }
2071
2072 return d->registry->waitForSource(timeout);
2073 }
2074
2075 /*!
2076 Connects a client node to the host node at \a address.
2077
2078 Connections will remain valid until the host node is deleted or no longer
2079 accessible over a network.
2080
2081 Once a client is connected to a host, valid Replicas can then be acquired
2082 if the corresponding Source is being remoted.
2083
2084 Return \c true on success, \c false otherwise (usually an unrecognized url,
2085 or connecting to already connected address).
2086 */
connectToNode(const QUrl & address)2087 bool QRemoteObjectNode::connectToNode(const QUrl &address)
2088 {
2089 Q_D(QRemoteObjectNode);
2090 if (!d->initConnection(address)) {
2091 d->setLastError(RegistryNotAcquired);
2092 return false;
2093 }
2094 return true;
2095 }
2096
2097 /*!
2098 \since 5.12
2099
2100 In order to \l QRemoteObjectNode::acquire() \l Replica objects over \l
2101 {External QIODevices}, Qt Remote Objects needs access to the communications
2102 channel (a \l QIODevice) between the respective nodes. It is the
2103 addClientSideConnection() call that enables this, taking the \a ioDevice as
2104 input. Any acquire() call made without calling addClientSideConnection will
2105 still work, but the Node will not be able to initialize the \l Replica
2106 without being provided the connection to the Host node.
2107
2108 \sa {QRemoteObjectHostBase::addHostSideConnection}
2109 */
addClientSideConnection(QIODevice * ioDevice)2110 void QRemoteObjectNode::addClientSideConnection(QIODevice *ioDevice)
2111 {
2112 Q_D(QRemoteObjectNode);
2113 ExternalIoDevice *device = new ExternalIoDevice(ioDevice, this);
2114 connect(device, &IoDeviceBase::readyRead, this, [d, device]() {
2115 d->onClientRead(device);
2116 });
2117 if (device->bytesAvailable())
2118 d->onClientRead(device);
2119 }
2120
2121 /*!
2122 \fn void QRemoteObjectNode::remoteObjectAdded(const QRemoteObjectSourceLocation &loc)
2123
2124 This signal is emitted whenever a new \l {Source} object is added to
2125 the Registry. The signal will not be emitted if there is no Registry set
2126 (i.e., Sources over connections made via connectToNode directly). The \a
2127 loc parameter contains the information about the added Source, including
2128 name, type and the QUrl of the hosting Node.
2129
2130 \sa remoteObjectRemoved(), instances()
2131 */
2132
2133 /*!
2134 \fn void QRemoteObjectNode::remoteObjectRemoved(const QRemoteObjectSourceLocation &loc)
2135
2136 This signal is emitted whenever there is a known \l {Source} object is
2137 removed from the Registry. The signal will not be emitted if there is no
2138 Registry set (i.e., Sources over connections made via connectToNode
2139 directly). The \a loc parameter contains the information about the removed
2140 Source, including name, type and the QUrl of the hosting Node.
2141
2142 \sa remoteObjectAdded, instances
2143 */
2144
2145 /*!
2146 \fn QStringList QRemoteObjectNode::instances() const
2147
2148 This templated function (taking a \l repc generated type as the template parameter) will
2149 return the list of names of every instance of that type on the Remote
2150 Objects network. For example, if you have a Shape class defined in a .rep file,
2151 and Circle and Square classes inherit from the Source definition, they can
2152 be shared on the Remote Objects network using \l {QRemoteObjectHostBase::enableRemoting} {enableRemoting}.
2153 \code
2154 Square square;
2155 Circle circle;
2156 myHost.enableRemoting(&square, "Square");
2157 myHost.enableRemoting(&circle, "Circle");
2158 \endcode
2159 Then instance can be used to find the available instances of Shape.
2160 \code
2161 QStringList instances = clientNode.instances<Shape>();
2162 // will return a QStringList containing "Circle" and "Square"
2163 auto instance1 = clientNode.acquire<Shape>("Circle");
2164 auto instance2 = clientNode.acquire<Shape>("Square");
2165 ...
2166 \endcode
2167 */
2168
2169 /*!
2170 \overload instances()
2171
2172 This convenience function provides the same result as the templated
2173 version, but takes the name of the \l {Source} class as a parameter (\a
2174 typeName) rather than deriving it from the class type.
2175 */
instances(const QString & typeName) const2176 QStringList QRemoteObjectNode::instances(const QString &typeName) const
2177 {
2178 Q_D(const QRemoteObjectNode);
2179 QStringList names;
2180 for (auto it = d->connectedSources.cbegin(), end = d->connectedSources.cend(); it != end; ++it) {
2181 if (it.value().typeName == typeName) {
2182 names << it.key();
2183 }
2184 }
2185 return names;
2186 }
2187
2188 /*!
2189 \keyword dynamic acquire
2190 Returns a QRemoteObjectDynamicReplica of the Source \a name.
2191 */
acquireDynamic(const QString & name)2192 QRemoteObjectDynamicReplica *QRemoteObjectNode::acquireDynamic(const QString &name)
2193 {
2194 return new QRemoteObjectDynamicReplica(this, name);
2195 }
2196
2197 /*!
2198 \qmlmethod bool Host::enableRemoting(object object, string name)
2199 Enables a host node to dynamically provide remote access to the QObject \a
2200 object. Client nodes connected to the node hosting this object may obtain
2201 Replicas of this Source.
2202
2203 The optional \a name defines the lookup-name under which the QObject can be acquired
2204 using \l QRemoteObjectNode::acquire() . If not explicitly set then the name
2205 given in the QCLASSINFO_REMOTEOBJECT_TYPE will be used. If no such macro
2206 was defined for the QObject then the \l QObject::objectName() is used.
2207
2208 Returns \c false if the current node is a client node, or if the QObject is already
2209 registered to be remoted, and \c true if remoting is successfully enabled
2210 for the dynamic QObject.
2211
2212 \sa disableRemoting()
2213 */
2214
2215 /*!
2216 Enables a host node to dynamically provide remote access to the QObject \a
2217 object. Client nodes connected to the node
2218 hosting this object may obtain Replicas of this Source.
2219
2220 The optional \a name defines the lookup-name under which the QObject can be acquired
2221 using \l QRemoteObjectNode::acquire() . If not explicitly set then the name
2222 given in the QCLASSINFO_REMOTEOBJECT_TYPE will be used. If no such macro
2223 was defined for the QObject then the \l QObject::objectName() is used.
2224
2225 Returns \c false if the current node is a client node, or if the QObject is already
2226 registered to be remoted, and \c true if remoting is successfully enabled
2227 for the dynamic QObject.
2228
2229 \sa disableRemoting()
2230 */
enableRemoting(QObject * object,const QString & name)2231 bool QRemoteObjectHostBase::enableRemoting(QObject *object, const QString &name)
2232 {
2233 Q_D(QRemoteObjectHostBase);
2234 if (!d->remoteObjectIo) {
2235 d->setLastError(OperationNotValidOnClientNode);
2236 return false;
2237 }
2238
2239 const QMetaObject *meta = object->metaObject();
2240 QString _name = name;
2241 QString typeName = getTypeNameAndMetaobjectFromClassInfo(meta);
2242 if (typeName.isEmpty()) { //This is a passed in QObject, use its API
2243 if (_name.isEmpty()) {
2244 _name = object->objectName();
2245 if (_name.isEmpty()) {
2246 d->setLastError(MissingObjectName);
2247 qCWarning(QT_REMOTEOBJECT) << qPrintable(objectName()) << "enableRemoting() Error: Unable to Replicate an object that does not have objectName() set.";
2248 return false;
2249 }
2250 }
2251 } else if (_name.isEmpty())
2252 _name = typeName;
2253 return d->remoteObjectIo->enableRemoting(object, meta, _name, typeName);
2254 }
2255
2256 /*!
2257 This overload of enableRemoting() is specific to \l QAbstractItemModel types
2258 (or any type derived from \l QAbstractItemModel). This is useful if you want
2259 to have a model and the HMI for the model in different processes.
2260
2261 The three required parameters are the \a model itself, the \a name by which
2262 to lookup the model, and the \a roles that should be exposed on the Replica
2263 side. If you want to synchronize selection between \l Source and \l
2264 Replica, the optional \a selectionModel parameter can be used. This is only
2265 recommended when using a single Replica.
2266
2267 Behind the scenes, Qt Remote Objects batches data() lookups and prefetches
2268 data when possible to make the model interaction as responsive as possible.
2269
2270 Returns \c false if the current node is a client node, or if the QObject is already
2271 registered to be remoted, and \c true if remoting is successfully enabled
2272 for the QAbstractItemModel.
2273
2274 \sa disableRemoting()
2275 */
enableRemoting(QAbstractItemModel * model,const QString & name,const QVector<int> roles,QItemSelectionModel * selectionModel)2276 bool QRemoteObjectHostBase::enableRemoting(QAbstractItemModel *model, const QString &name, const QVector<int> roles, QItemSelectionModel *selectionModel)
2277 {
2278 //This looks complicated, but hopefully there is a way to have an adapter be a template
2279 //parameter and this makes sure that is supported.
2280 QObject *adapter = QAbstractItemModelSourceAdapter::staticMetaObject.newInstance(Q_ARG(QAbstractItemModel*, model),
2281 Q_ARG(QItemSelectionModel*, selectionModel),
2282 Q_ARG(QVector<int>, roles));
2283 QAbstractItemAdapterSourceAPI<QAbstractItemModel, QAbstractItemModelSourceAdapter> *api =
2284 new QAbstractItemAdapterSourceAPI<QAbstractItemModel, QAbstractItemModelSourceAdapter>(name);
2285 if (!this->objectName().isEmpty())
2286 adapter->setObjectName(this->objectName().append(QLatin1String("Adapter")));
2287 return enableRemoting(model, api, adapter);
2288 }
2289
2290 /*!
2291 \fn template <template <typename> class ApiDefinition, typename ObjectType> bool QRemoteObjectHostBase::enableRemoting(ObjectType *object)
2292
2293 This templated function overload enables a host node to provide remote
2294 access to a QObject \a object with a specified (and compile-time checked)
2295 interface. Client nodes connected to the node hosting this object may
2296 obtain Replicas of this Source.
2297
2298 This is best illustrated by example:
2299 \code
2300 #include "rep_TimeModel_source.h"
2301 MinuteTimer timer;
2302 hostNode.enableRemoting<MinuteTimerSourceAPI>(&timer);
2303 \endcode
2304
2305 Here the MinuteTimerSourceAPI is the set of Signals/Slots/Properties
2306 defined by the TimeModel.rep file. Compile time checks are made to verify
2307 the input QObject can expose the requested API, it will fail to compile
2308 otherwise. This allows a subset of \a object 's interface to be exposed,
2309 and allows the types of conversions supported by Signal/Slot connections.
2310
2311 Returns \c false if the current node is a client node, or if the QObject is
2312 already registered to be remoted, and \c true if remoting is successfully
2313 enabled for the QObject.
2314
2315 \sa disableRemoting()
2316 */
2317
2318 /*!
2319 \internal
2320 Enables a host node to provide remote access to a QObject \a object
2321 with the API defined by \a api. Client nodes connected to the node
2322 hosting this object may obtain Replicas of this Source.
2323
2324 Returns \c false if the current node is a client node, or if the QObject is
2325 already registered to be remoted, and \c true if remoting is successfully
2326 enabled for the QObject.
2327
2328 \sa disableRemoting()
2329 */
enableRemoting(QObject * object,const SourceApiMap * api,QObject * adapter)2330 bool QRemoteObjectHostBase::enableRemoting(QObject *object, const SourceApiMap *api, QObject *adapter)
2331 {
2332 Q_D(QRemoteObjectHostBase);
2333 return d->remoteObjectIo->enableRemoting(object, api, adapter);
2334 }
2335
2336 /*!
2337 \qmlmethod bool Host::disableRemoting(object remoteObject)
2338 Disables remote access for the QObject \a remoteObject. Returns \c false if
2339 the current node is a client node or if the \a remoteObject is not
2340 registered, and returns \c true if remoting is successfully disabled for
2341 the Source object.
2342
2343 \warning Replicas of this object will no longer be valid after calling this method.
2344
2345 \sa enableRemoting()
2346 */
2347
2348 /*!
2349 Disables remote access for the QObject \a remoteObject. Returns \c false if
2350 the current node is a client node or if the \a remoteObject is not
2351 registered, and returns \c true if remoting is successfully disabled for
2352 the Source object.
2353
2354 \warning Replicas of this object will no longer be valid after calling this method.
2355
2356 \sa enableRemoting()
2357 */
disableRemoting(QObject * remoteObject)2358 bool QRemoteObjectHostBase::disableRemoting(QObject *remoteObject)
2359 {
2360 Q_D(QRemoteObjectHostBase);
2361 if (!d->remoteObjectIo) {
2362 d->setLastError(OperationNotValidOnClientNode);
2363 return false;
2364 }
2365
2366 if (!d->remoteObjectIo->disableRemoting(remoteObject)) {
2367 d->setLastError(SourceNotRegistered);
2368 return false;
2369 }
2370
2371 return true;
2372 }
2373
2374 /*!
2375 \since 5.12
2376
2377 In order to \l QRemoteObjectHost::enableRemoting() \l Source objects over
2378 \l {External QIODevices}, Qt Remote Objects needs access to the
2379 communications channel (a \l QIODevice) between the respective nodes. It is
2380 the addHostSideConnection() call that enables this on the \l Source side,
2381 taking the \a ioDevice as input. Any enableRemoting() call will still work
2382 without calling addHostSideConnection, but the Node will not be able to
2383 share the \l Source objects without being provided the connection to
2384 the Replica node. Before calling this function you must call
2385 \l {QRemoteObjectHost::}{setHostUrl}() with a unique URL and
2386 \l {QRemoteObjectHost::}{AllowExternalRegistration}.
2387
2388 \sa addClientSideConnection
2389 */
addHostSideConnection(QIODevice * ioDevice)2390 void QRemoteObjectHostBase::addHostSideConnection(QIODevice *ioDevice)
2391 {
2392 Q_D(QRemoteObjectHostBase);
2393 if (!d->remoteObjectIo)
2394 d->remoteObjectIo = new QRemoteObjectSourceIo(this);
2395 ExternalIoDevice *device = new ExternalIoDevice(ioDevice, this);
2396 return d->remoteObjectIo->newConnection(device);
2397 }
2398
2399 /*!
2400 Returns a pointer to a Replica which is specifically derived from \l
2401 QAbstractItemModel. The \a name provided must match the name used with the
2402 matching \l {QRemoteObjectHostBase::}{enableRemoting} that put
2403 the Model on the network. The returned model will be empty until it is
2404 initialized with the \l Source.
2405 */
acquireModel(const QString & name,QtRemoteObjects::InitialAction action,const QVector<int> & rolesHint)2406 QAbstractItemModelReplica *QRemoteObjectNode::acquireModel(const QString &name, QtRemoteObjects::InitialAction action, const QVector<int> &rolesHint)
2407 {
2408 QAbstractItemModelReplicaImplementation *rep = acquire<QAbstractItemModelReplicaImplementation>(name);
2409 return new QAbstractItemModelReplica(rep, action, rolesHint);
2410 }
2411
QRemoteObjectHostBasePrivate()2412 QRemoteObjectHostBasePrivate::QRemoteObjectHostBasePrivate()
2413 : QRemoteObjectNodePrivate()
2414 , remoteObjectIo(nullptr)
2415 { }
2416
~QRemoteObjectHostBasePrivate()2417 QRemoteObjectHostBasePrivate::~QRemoteObjectHostBasePrivate()
2418 { }
2419
QRemoteObjectHostPrivate()2420 QRemoteObjectHostPrivate::QRemoteObjectHostPrivate()
2421 : QRemoteObjectHostBasePrivate()
2422 { }
2423
~QRemoteObjectHostPrivate()2424 QRemoteObjectHostPrivate::~QRemoteObjectHostPrivate()
2425 { }
2426
QRemoteObjectRegistryHostPrivate()2427 QRemoteObjectRegistryHostPrivate::QRemoteObjectRegistryHostPrivate()
2428 : QRemoteObjectHostBasePrivate()
2429 , registrySource(nullptr)
2430 { }
2431
~QRemoteObjectRegistryHostPrivate()2432 QRemoteObjectRegistryHostPrivate::~QRemoteObjectRegistryHostPrivate()
2433 { }
2434
ProxyInfo(QRemoteObjectNode * node,QRemoteObjectHostBase * parent,QRemoteObjectHostBase::RemoteObjectNameFilter filter)2435 ProxyInfo::ProxyInfo(QRemoteObjectNode *node, QRemoteObjectHostBase *parent,
2436 QRemoteObjectHostBase::RemoteObjectNameFilter filter)
2437 : QObject(parent)
2438 , proxyNode(node)
2439 , parentNode(parent)
2440 , proxyFilter(filter)
2441 {
2442 const auto registry = node->registry();
2443 proxyNode->setObjectName(QString::fromLatin1("_ProxyNode"));
2444
2445 connect(registry, &QRemoteObjectRegistry::remoteObjectAdded, this,
2446 [this](const QRemoteObjectSourceLocation &entry)
2447 {
2448 this->proxyObject(entry, ProxyDirection::Forward);
2449 });
2450 connect(registry, &QRemoteObjectRegistry::remoteObjectRemoved, this,
2451 &ProxyInfo::unproxyObject);
2452 connect(registry, &QRemoteObjectRegistry::initialized, this, [registry, this]() {
2453 QRemoteObjectSourceLocations locations = registry->sourceLocations();
2454 QRemoteObjectSourceLocations::const_iterator i = locations.constBegin();
2455 while (i != locations.constEnd()) {
2456 proxyObject(QRemoteObjectSourceLocation(i.key(), i.value()));
2457 ++i;
2458 }
2459 });
2460
2461 connect(registry, &QRemoteObjectRegistry::stateChanged, this,
2462 [this](QRemoteObjectRegistry::State state, QRemoteObjectRegistry::State /*oldState*/) {
2463 if (state != QRemoteObjectRegistry::Suspect)
2464 return;
2465 // unproxy all objects
2466 for (ProxyReplicaInfo* info : proxiedReplicas)
2467 disableAndDeleteObject(info);
2468 proxiedReplicas.clear();
2469 });
2470 }
2471
~ProxyInfo()2472 ProxyInfo::~ProxyInfo() {
2473 for (ProxyReplicaInfo* info : proxiedReplicas)
2474 delete info;
2475 }
2476
setReverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter)2477 bool ProxyInfo::setReverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter)
2478 {
2479 if (qobject_cast<QRemoteObjectRegistryHost *>(parentNode) == nullptr) {
2480 qWarning() << "Setting up reverseProxy() can only be done on a Registry node.";
2481 return false;
2482 }
2483 const auto registry = parentNode->registry();
2484 this->reverseFilter = filter;
2485
2486 connect(registry, &QRemoteObjectRegistry::remoteObjectAdded, this,
2487 [this](const QRemoteObjectSourceLocation &entry)
2488 {
2489 this->proxyObject(entry, ProxyDirection::Reverse);
2490 });
2491 connect(registry, &QRemoteObjectRegistry::remoteObjectRemoved, this,
2492 &ProxyInfo::unproxyObject);
2493 connect(registry, &QRemoteObjectRegistry::initialized, this, [registry, this]() {
2494 QRemoteObjectSourceLocations locations = registry->sourceLocations();
2495 QRemoteObjectSourceLocations::const_iterator i = locations.constBegin();
2496 while (i != locations.constEnd()) {
2497 proxyObject(QRemoteObjectSourceLocation(i.key(), i.value()), ProxyDirection::Reverse);
2498 ++i;
2499 }
2500 });
2501
2502 return true;
2503 }
2504
proxyObject(const QRemoteObjectSourceLocation & entry,ProxyDirection direction)2505 void ProxyInfo::proxyObject(const QRemoteObjectSourceLocation &entry, ProxyDirection direction)
2506 {
2507 const QString name = entry.first;
2508 const QString typeName = entry.second.typeName;
2509
2510 if (direction == ProxyDirection::Forward) {
2511 // If we are using the reverse proxy, this can be called when reverse proxy objects are added
2512 // Don't try to proxy those back. We can detect this because the hosting node will be our proxyNode.
2513 auto host = qobject_cast<QRemoteObjectHost *>(proxyNode);
2514 if (host && entry.second.hostUrl == host->hostUrl())
2515 return;
2516 if (!proxyFilter(name, typeName))
2517 return;
2518 Q_ASSERT(!proxiedReplicas.contains(name));
2519
2520 qCDebug(QT_REMOTEOBJECT) << "Starting proxy for" << name << "from" << entry.second.hostUrl;
2521
2522 if (entry.second.typeName == QAIMADAPTER()) {
2523 QAbstractItemModelReplica *rep = proxyNode->acquireModel(name);
2524 proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
2525 connect(rep, &QAbstractItemModelReplica::initialized, this,
2526 [rep, name, this]() { this->parentNode->enableRemoting(rep, name, QVector<int>()); });
2527 } else {
2528 QRemoteObjectDynamicReplica *rep = proxyNode->acquireDynamic(name);
2529 proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
2530 connect(rep, &QRemoteObjectDynamicReplica::initialized, this,
2531 [rep, name, this]() { this->parentNode->enableRemoting(rep, name); });
2532 }
2533 } else {
2534 // If we are using the reverse proxy, this can be called when proxy objects are added
2535 // Don't try to proxy those back. We can detect this because the hosting node will be the parentNode.
2536 // Since we know the parentNode has to be a RegistryNode for reverse proxy to work, we compare against
2537 // the registryUrl().
2538 if (entry.second.hostUrl == parentNode->registryUrl())
2539 return;
2540 if (!reverseFilter(name, typeName))
2541 return;
2542 Q_ASSERT(!proxiedReplicas.contains(name));
2543
2544 qCDebug(QT_REMOTEOBJECT) << "Starting reverse proxy for" << name << "from" << entry.second.hostUrl;
2545
2546 if (entry.second.typeName == QAIMADAPTER()) {
2547 QAbstractItemModelReplica *rep = this->parentNode->acquireModel(name);
2548 proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
2549 connect(rep, &QAbstractItemModelReplica::initialized, this,
2550 [rep, name, this]()
2551 {
2552 QRemoteObjectHostBase *host = qobject_cast<QRemoteObjectHostBase *>(this->proxyNode);
2553 Q_ASSERT(host);
2554 host->enableRemoting(rep, name, QVector<int>());
2555 });
2556 } else {
2557 QRemoteObjectDynamicReplica *rep = this->parentNode->acquireDynamic(name);
2558 proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
2559 connect(rep, &QRemoteObjectDynamicReplica::initialized, this,
2560 [rep, name, this]()
2561 {
2562 QRemoteObjectHostBase *host = qobject_cast<QRemoteObjectHostBase *>(this->proxyNode);
2563 Q_ASSERT(host);
2564 host->enableRemoting(rep, name);
2565 });
2566 }
2567 }
2568
2569 }
2570
unproxyObject(const QRemoteObjectSourceLocation & entry)2571 void ProxyInfo::unproxyObject(const QRemoteObjectSourceLocation &entry)
2572 {
2573 const QString name = entry.first;
2574
2575 if (proxiedReplicas.contains(name)) {
2576 qCDebug(QT_REMOTEOBJECT) << "Stopping proxy for" << name;
2577 auto const info = proxiedReplicas.take(name);
2578 disableAndDeleteObject(info);
2579 }
2580 }
2581
disableAndDeleteObject(ProxyReplicaInfo * info)2582 void ProxyInfo::disableAndDeleteObject(ProxyReplicaInfo* info)
2583 {
2584 if (info->direction == ProxyDirection::Forward)
2585 this->parentNode->disableRemoting(info->replica);
2586 else {
2587 QRemoteObjectHostBase *host = qobject_cast<QRemoteObjectHostBase *>(this->proxyNode);
2588 Q_ASSERT(host);
2589 host->disableRemoting(info->replica);
2590 }
2591 delete info;
2592 }
2593
2594
2595 QT_END_NAMESPACE
2596
2597 #include "moc_qremoteobjectnode.cpp"
2598