summaryrefslogtreecommitdiffstats
path: root/src/remoteobjects/qremoteobjectnode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/remoteobjects/qremoteobjectnode.cpp')
-rw-r--r--src/remoteobjects/qremoteobjectnode.cpp309
1 files changed, 283 insertions, 26 deletions
diff --git a/src/remoteobjects/qremoteobjectnode.cpp b/src/remoteobjects/qremoteobjectnode.cpp
index 39c5ea7..3d668ad 100644
--- a/src/remoteobjects/qremoteobjectnode.cpp
+++ b/src/remoteobjects/qremoteobjectnode.cpp
@@ -163,6 +163,172 @@ void QRemoteObjectNode::setHeartbeatInterval(int interval)
}
/*!
+ \since 5.11
+ \brief Forward Remote Objects from another network
+
+ The proxy functionality is useful when you want to share \l Source objects
+ over multiple networks. For instance, if you have an embedded target using
+ target-only connections (like local) and you want to make some of those
+ same objects available externally.
+
+ As a concrete example, say you have a set of processes talking to each
+ other on your target hardware using a registry, with the \l Registry at
+ "local:registry" and separate processes using a node at "local:MyHost" that
+ holds \l Source objects. If you wanted to access these objects, but over
+ tcp, you could create a new proxyNode like so:
+
+\code
+ // myInternalHost is a node only visible on the device...
+ QRemoteObjectHost myInternalHost("local:MyHost");
+ myInternalHost.enableRemoting<SomeObject>(&someObject);
+
+ // Regular host node, listening on port 12123, so visible to other
+ // devices
+ QRemoteObjectHost proxyNode("tcp://localhost:12123");
+
+ // Enable proxying objects from nodes on the local machine's internal
+ // QtRO bus
+ proxyNode.proxy("local:registry");
+\endcode
+
+ And from another device you create another node:
+
+\code
+ // NB: localhost resolves to a different ip address than proxyNode
+ QRemoteObjectHost nodeOnRemoteDevice("tcp://localhost:23234");
+
+ // Connect to the target's proxyNode directly, or use a tcp registry...
+ nodeOnRemoteDevice.connectToNode("tcp://<target device>:12123");
+
+ // Because of the proxy, we can get the object over tcp/ip port 12123,
+ // even though we can't connect directly to "local:MyHost"
+ SomeObject *so = nodeOnRemoteDevice.acquire<SomeObject>();
+\endcode
+
+ This would (internally) create a node in proxyNode, which (again
+ internally/automatically) connects to the provided registry (given by the
+ \a registryUrl parameter, "local:registry" in this example). Whenever
+ local:registry emits the \l remoteObjectAdded signal, the \l
+ QRemoteObjectSourceLocation is passed to the \a filter given to the proxy
+ call. If this method returns true (the default filter simply returns true
+ without any filtering), the object is acquired() from the internal node and
+ enableRemoting() (once the replica is initialized) is called on proxyNode.
+
+ If a \a hostUrl is provided (which is required to enable reverseProxy, but
+ not needed otherwise), the internal node will be a \l QRemoteObjectHostNode
+ configured with the provided address. If no \a hostUrl is provided, the
+ internal node will be a QRemoteObjectNode (not HostNode).
+
+ \sa reverseProxy()
+*/
+bool QRemoteObjectHostBase::proxy(const QUrl &registryUrl, const QUrl &hostUrl, RemoteObjectNameFilter filter)
+{
+ Q_D(QRemoteObjectHostBase);
+ if (!registryUrl.isValid() || !QtROClientFactory::instance()->isValid(registryUrl)) {
+ qROWarning(this) << "Can't proxy to registryUrl (invalid url or schema)" << registryUrl;
+ return false;
+ }
+
+ if (!hostUrl.isEmpty() && !QtROClientFactory::instance()->isValid(hostUrl)) {
+ qROWarning(this) << "Can't proxy using hostUrl (invalid schema)" << hostUrl;
+ return false;
+ }
+
+ if (d->proxyInfo) {
+ qROWarning(this) << "Proxying from multiple objects is currently not supported.";
+ return false;
+ }
+
+ QRemoteObjectNode *node;
+ if (hostUrl.isEmpty()) {
+ node = new QRemoteObjectNode(registryUrl);
+ } else {
+ node = new QRemoteObjectHost(hostUrl, registryUrl);
+ }
+ d->proxyInfo = new ProxyInfo(node, this, filter);
+ return true;
+}
+
+/*!
+ \since 5.11
+ \brief Forward Remote Objects to another network
+
+ The reverseProxy() function allows the \l proxy() functionality to be
+ extended, in effect mirroring the proxy functionality in the "reverse"
+ direction. These are distinct, because node communication is not symmetric,
+ one side calls enableRemoting() with a \l Source object, the other side
+ calls acquire() to get a \l Replica. Using \l proxy() allows you to
+ "observe" objects on a target device remotely via acquire, but it does not
+ allow off-target \l Source objects to be acquired from the device's local:*
+ network. That is where \l reverseProxy() comes in. If a proxyNode is
+ created like so:
+
+\code
+ // myInternalHost is a node only visible on the device...
+ QRemoteObjectHost myInternalHost("local:MyHost");
+
+ // Regular host node, listening on port 12123, so visible to other
+ // devices
+ QRemoteObjectHost proxyNode("tcp://localhost:12123");
+
+ // Enable proxying objects from nodes on the local machine's internal
+ // QtRO bus. Note the hostUrl parameter is now needed.
+ proxyNode.proxy("local:registry", "local:fromProxy");
+ proxyNode.reverseProxy();
+\endcode
+
+ And from another device you create another node:
+
+\code
+ // NB: localhost resolves to a different ip address than proxyNode
+ QRemoteObjectHost nodeOnRemoteDevice("tcp://localhost:23234");
+
+ // Connect to the target's proxyNode directly, or use a tcp registry...
+ nodeOnRemoteDevice.connectToNode("tcp://<target device>:12123");
+
+ // Because of the reverseProxy, we can expose objects on this device
+ // and they will make their way to proxyNode...
+ nodeOnRemoteDevice.enableRemoting<OtherObject>(&otherObject);
+\endcode
+
+\code
+ // Acquire() can now see the objects on other devices through proxyNode,
+ // due to the reverseProxy call.
+ OtherObject *oo = myInternalHost.acquire<OtherObject>();
+\endcode
+
+ While the \l proxy() functionality allows \l Source objects on another
+ network to be acquired(), reverseProxy() allows \l Source objects to be
+ "pushed" to an otherwise inaccessible network.
+
+ Note: \l proxy() needs to be called before \l reverseProxy(), and a \a
+ hostUrl needs to be provided to \l proxy for \l reverseProxy() to work. The
+ \l reverseProxy() method allows a separate \a filter to be applied. This
+ reverseProxy specific filter will receive notifications of new \l Source
+ objects on proxyNode and acquire them on the internal node if they pass the
+ reverseFilter.
+
+ \sa proxy()
+*/
+bool QRemoteObjectHostBase::reverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter)
+{
+ Q_D(QRemoteObjectHostBase);
+
+ if (!d->proxyInfo) {
+ qROWarning(this) << "proxy() needs to be called before setting up reverse proxy.";
+ return false;
+ }
+
+ QRemoteObjectHost *host = qobject_cast<QRemoteObjectHost *>(d->proxyInfo->proxyNode);
+ if (!host) {
+ qROWarning(this) << "proxy() needs called with host-url to enable reverse proxy.";
+ return false;
+ }
+
+ return d->proxyInfo->setReverseProxy(filter);
+}
+
+/*!
\internal The replica needs to have a default constructor to be able
to create a replica from QML. In order for it to be properly
constructed, there needs to be a way to associate the replica with a
@@ -618,30 +784,6 @@ void QRemoteObjectNodePrivate::handleReplicaConnection(const QByteArray &sourceS
rep->setState(QRemoteObjectReplica::SignatureMismatch);
return;
}
- if (!rep->m_metaObject) {
- rep->setConnection(connection);
- return;
- }
- for (int i = rep->m_metaObject->propertyOffset(); i < rep->m_metaObject->propertyCount(); ++i) {
- const QMetaProperty property = rep->m_metaObject->property(i);
- if (!QMetaType::typeFlags(property.userType()).testFlag(QMetaType::PointerToQObject))
- continue;
-
- const auto type = property.typeName();
- const size_t len = strlen(type);
- if (len > 8 && strncmp(&type[len-8], "Replica*", 8) == 0) {
- auto propertyMeta = QMetaType::metaObjectForType(property.userType());
- QString name;
- if (propertyMeta->inherits(&QAbstractItemModel::staticMetaObject))
- name = MODEL().arg(QString::fromLatin1(property.name()));
- else
- name = CLASS().arg(QString::fromLatin1(property.name()));
- if (replicas.contains(name))
- handleReplicaConnection(name);
- else
- qROPrivWarning() << "Child type" << name << "not available. Objects:" << replicas.keys();
- }
- }
rep->setConnection(connection);
}
@@ -1337,6 +1479,8 @@ QVariant QRemoteObjectNodePrivate::handlePointerToQObjectProperty(QConnectedRepl
retval = rep->getProperty(index); //Use existing value so changed signal isn't emitted
QSharedPointer<QConnectedReplicaImplementation> childRep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(childInfo.name).toStrongRef());
+ if (childRep->connectionToSource.isNull())
+ childRep->connectionToSource = rep->connectionToSource;
if (childRep->needsDynamicInitialization()) {
if (childInfo.classDefinition.isEmpty())
childRep->setDynamicMetaObject(dynamicTypeManager.metaObjectForType(childInfo.typeName));
@@ -1344,9 +1488,12 @@ QVariant QRemoteObjectNodePrivate::handlePointerToQObjectProperty(QConnectedRepl
QDataStream in(childInfo.classDefinition);
childRep->setDynamicMetaObject(dynamicTypeManager.addDynamicType(in));
}
+ handlePointerToQObjectProperties(childRep.data(), childInfo.parameters);
+ childRep->setDynamicProperties(childInfo.parameters);
+ } else {
+ handlePointerToQObjectProperties(childRep.data(), childInfo.parameters);
+ childRep->initialize(childInfo.parameters);
}
- handlePointerToQObjectProperties(childRep.data(), childInfo.parameters);
- childRep->setDynamicProperties(childInfo.parameters);
}
return retval;
@@ -1641,6 +1788,116 @@ QRemoteObjectRegistryHostPrivate::QRemoteObjectRegistryHostPrivate()
, registrySource(nullptr)
{ }
+ProxyInfo::ProxyInfo(QRemoteObjectNode *node, QRemoteObjectHostBase *parent,
+ QRemoteObjectHostBase::RemoteObjectNameFilter filter)
+ : QObject(parent)
+ , proxyNode(node)
+ , parentNode(parent)
+ , proxyFilter(filter)
+{
+ const auto registry = node->registry();
+ proxyNode->setObjectName(QString::fromLatin1("_ProxyNode"));
+
+ connect(registry, &QRemoteObjectRegistry::remoteObjectAdded, this,
+ [this](const QRemoteObjectSourceLocation &entry)
+ {
+ this->proxyObject(entry, ProxyDirection::Forward);
+ });
+ connect(registry, &QRemoteObjectRegistry::remoteObjectRemoved, this,
+ &ProxyInfo::unproxyObject);
+ connect(registry, &QRemoteObjectRegistry::initialized, this, [registry, this]() {
+ QRemoteObjectSourceLocations locations = registry->sourceLocations();
+ QRemoteObjectSourceLocations::const_iterator i = locations.constBegin();
+ while (i != locations.constEnd()) {
+ proxyObject(QRemoteObjectSourceLocation(i.key(), i.value()));
+ ++i;
+ }
+ });
+}
+
+ProxyInfo::~ProxyInfo() {
+ for (ProxyReplicaInfo* info : proxiedReplicas)
+ delete info;
+}
+
+bool ProxyInfo::setReverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter)
+{
+ const auto registry = proxyNode->registry();
+ this->reverseFilter = filter;
+
+ connect(registry, &QRemoteObjectRegistry::remoteObjectAdded, this,
+ [this](const QRemoteObjectSourceLocation &entry)
+ {
+ this->proxyObject(entry, ProxyDirection::Reverse);
+ });
+ connect(registry, &QRemoteObjectRegistry::remoteObjectRemoved, this,
+ &ProxyInfo::unproxyObject);
+ connect(registry, &QRemoteObjectRegistry::initialized, this, [registry, this]() {
+ QRemoteObjectSourceLocations locations = registry->sourceLocations();
+ QRemoteObjectSourceLocations::const_iterator i = locations.constBegin();
+ while (i != locations.constEnd()) {
+ proxyObject(QRemoteObjectSourceLocation(i.key(), i.value()), ProxyDirection::Reverse);
+ ++i;
+ }
+ });
+
+ return true;
+}
+
+void ProxyInfo::proxyObject(const QRemoteObjectSourceLocation &entry, ProxyDirection direction)
+{
+ const QString name = entry.first;
+ const QString typeName = entry.second.typeName;
+
+ if (direction == ProxyDirection::Forward) {
+ if (!proxyFilter(name, typeName))
+ return;
+
+ qCDebug(QT_REMOTEOBJECT) << "Starting proxy for" << name << "from" << entry.second.hostUrl;
+
+ QRemoteObjectDynamicReplica *rep = proxyNode->acquireDynamic(name);
+ Q_ASSERT(!proxiedReplicas.contains(name));
+ proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
+ connect(rep, &QRemoteObjectDynamicReplica::initialized, this,
+ [rep, name, this]() { this->parentNode->enableRemoting(rep, name); });
+ } else {
+ if (!reverseFilter(name, typeName))
+ return;
+
+ qCDebug(QT_REMOTEOBJECT) << "Starting reverse proxy for" << name << "from" << entry.second.hostUrl;
+
+ QRemoteObjectDynamicReplica *rep = this->parentNode->acquireDynamic(name);
+ Q_ASSERT(!proxiedReplicas.contains(name));
+ proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
+ connect(rep, &QRemoteObjectDynamicReplica::initialized, this,
+ [rep, name, this]()
+ {
+ QRemoteObjectHostBase *host = qobject_cast<QRemoteObjectHostBase *>(this->proxyNode);
+ Q_ASSERT(host);
+ host->enableRemoting(rep, name);
+ });
+ }
+
+}
+
+void ProxyInfo::unproxyObject(const QRemoteObjectSourceLocation &entry)
+{
+ const QString name = entry.first;
+
+ if (proxiedReplicas.contains(name)) {
+ qCDebug(QT_REMOTEOBJECT) << "Stopping proxy for" << name;
+ auto const info = proxiedReplicas.take(name);
+ if (info->direction == ProxyDirection::Forward)
+ this->parentNode->disableRemoting(info->replica);
+ else {
+ QRemoteObjectHostBase *host = qobject_cast<QRemoteObjectHostBase *>(this->proxyNode);
+ Q_ASSERT(host);
+ host->disableRemoting(info->replica);
+ }
+ delete info;
+ }
+}
+
QT_END_NAMESPACE
#include "moc_qremoteobjectnode.cpp"