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

source code of qtremoteobjects/src/remoteobjects/qremoteobjectnode.cpp