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

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