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

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