1// Copyright (C) 2017 Ford Motor Company
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qremoteobjectsourceio_p.h"
5
6#include "qremoteobjectpacket_p.h"
7#include "qremoteobjectsource_p.h"
8#include "qremoteobjectnode_p.h"
9#include "qremoteobjectpendingcall.h"
10#include "qtremoteobjectglobal.h"
11#include "qconnection_local_backend_p.h"
12
13#include <QtCore/qstringlist.h>
14
15QT_BEGIN_NAMESPACE
16
17using namespace QtRemoteObjects;
18
19QRemoteObjectSourceIo::QRemoteObjectSourceIo(const QUrl &address, QObject *parent)
20 : QObject(parent)
21 , m_server(QtROServerFactory::instance()->isValid(url: address) ?
22 QtROServerFactory::instance()->create(url: address, parent: this) : nullptr)
23 , m_address(address)
24{
25 if (m_server == nullptr)
26 qRODebug(this) << "Using" << m_address << "as external url.";
27}
28
29QRemoteObjectSourceIo::QRemoteObjectSourceIo(QObject *parent)
30 : QObject(parent)
31 , m_server(nullptr)
32{
33}
34
35QRemoteObjectSourceIo::~QRemoteObjectSourceIo()
36{
37 qDeleteAll(c: m_sourceRoots.values());
38}
39
40bool QRemoteObjectSourceIo::startListening()
41{
42 if (!m_server->listen(address: m_address)) {
43 qROCritical(this) << "Listen failed for URL:" << m_address;
44 qROCritical(this) << m_server->serverError();
45 return false;
46 }
47
48 qRODebug(this) << "QRemoteObjectSourceIo is Listening" << m_address;
49 connect(sender: m_server.data(), signal: &QConnectionAbstractServer::newConnection, context: this,
50 slot: &QRemoteObjectSourceIo::handleConnection);
51 return true;
52}
53
54bool QRemoteObjectSourceIo::enableRemoting(QObject *object, const QMetaObject *meta, const QString &name, const QString &typeName)
55{
56 if (m_sourceRoots.contains(key: name)) {
57 qROWarning(this) << "Tried to register QRemoteObjectRootSource twice" << name;
58 return false;
59 }
60
61 return enableRemoting(object, api: new DynamicApiMap(object, meta, name, typeName));
62}
63
64bool QRemoteObjectSourceIo::enableRemoting(QObject *object, const SourceApiMap *api, QObject *adapter)
65{
66 const QString name = api->name();
67 if (!api->isDynamic() && m_sourceRoots.contains(key: name)) {
68 qROWarning(this) << "Tried to register QRemoteObjectRootSource twice" << name;
69 return false;
70 }
71
72 new QRemoteObjectRootSource(object, api, adapter, this);
73 m_codec->serializeObjectListPacket({QRemoteObjectPackets::ObjectInfo{.name: api->name(), .typeName: api->typeName(), .signature: api->objectSignature()}});
74 m_codec->send(connections: m_connections);
75 if (const int count = m_connections.size())
76 qRODebug(this) << "Wrote new QObjectListPacket for" << api->name() << "to" << count << "connections";
77 return true;
78}
79
80bool QRemoteObjectSourceIo::disableRemoting(QObject *object)
81{
82 QRemoteObjectRootSource *source = m_objectToSourceMap.take(key: object);
83 if (!source)
84 return false;
85
86 delete source;
87 return true;
88}
89
90void QRemoteObjectSourceIo::setSocketOptions(QLocalServer::SocketOptions options)
91{
92 if (!m_server.isNull()) {
93 LocalServerImpl *server = qobject_cast<LocalServerImpl *>(object: m_server.get());
94 if (server != nullptr) {
95 server->setSocketOptions(options);
96 } else {
97 qROWarning(this)
98 << "Attempting to set socket options to a backend that is non-localserver";
99 }
100 }
101}
102
103void QRemoteObjectSourceIo::registerSource(QRemoteObjectSourceBase *source)
104{
105 Q_ASSERT(source);
106 const QString &name = source->name();
107 m_sourceObjects[name] = source;
108 if (source->isRoot()) {
109 QRemoteObjectRootSource *root = static_cast<QRemoteObjectRootSource *>(source);
110 qRODebug(this) << "Registering" << name;
111 m_sourceRoots[name] = root;
112 m_objectToSourceMap[source->m_object] = root;
113 if (serverAddress().isValid()) {
114 const auto &type = source->m_api->typeName();
115 emit remoteObjectAdded(qMakePair(value1: name, value2: QRemoteObjectSourceLocationInfo(type, serverAddress())));
116 }
117 }
118}
119
120void QRemoteObjectSourceIo::unregisterSource(QRemoteObjectSourceBase *source)
121{
122 Q_ASSERT(source);
123 const QString &name = source->name();
124 m_sourceObjects.remove(key: name);
125 if (source->isRoot()) {
126 const auto type = source->m_api->typeName();
127 m_objectToSourceMap.remove(key: source->m_object);
128 m_sourceRoots.remove(key: name);
129 if (serverAddress().isValid())
130 emit remoteObjectRemoved(qMakePair(value1: name, value2: QRemoteObjectSourceLocationInfo(type, serverAddress())));
131 }
132}
133
134void QRemoteObjectSourceIo::onServerDisconnect(QObject *conn)
135{
136 QtROIoDeviceBase *connection = qobject_cast<QtROIoDeviceBase*>(object: conn);
137 m_connections.remove(value: connection);
138
139 qRODebug(this) << "OnServerDisconnect";
140
141 for (QRemoteObjectRootSource *root : std::as_const(t&: m_sourceRoots))
142 root->removeListener(io: connection);
143
144 const QUrl location = m_registryMapping.value(key: connection);
145 emit serverRemoved(url: location);
146 m_registryMapping.remove(key: connection);
147 connection->close();
148 connection->deleteLater();
149}
150
151void QRemoteObjectSourceIo::onServerRead(QObject *conn)
152{
153 // Assert the invariant here conn is of type QIODevice
154 QtROIoDeviceBase *connection = qobject_cast<QtROIoDeviceBase*>(object: conn);
155 QRemoteObjectPacketTypeEnum packetType;
156
157 do {
158
159 if (!connection->read(packetType, m_rxName))
160 return;
161
162 using namespace QRemoteObjectPackets;
163
164 switch (packetType) {
165 case Ping:
166 m_codec->serializePongPacket(name: m_rxName);
167 m_codec->send(connection);
168 break;
169 case AddObject:
170 {
171 bool isDynamic;
172 m_codec->deserializeAddObjectPacket(connection->d_func()->stream(), isDynamic);
173 qRODebug(this) << "AddObject" << m_rxName << isDynamic;
174 if (m_sourceRoots.contains(key: m_rxName)) {
175 QRemoteObjectRootSource *root = m_sourceRoots[m_rxName];
176 root->addListener(io: connection, dynamic: isDynamic);
177 } else {
178 qROWarning(this) << "Request to attach to non-existent RemoteObjectSource:" << m_rxName;
179 }
180 break;
181 }
182 case RemoveObject:
183 {
184 qRODebug(this) << "RemoveObject" << m_rxName;
185 if (m_sourceRoots.contains(key: m_rxName)) {
186 QRemoteObjectRootSource *root = m_sourceRoots[m_rxName];
187 const int count = root->removeListener(io: connection);
188 Q_UNUSED(count);
189 //TODO - possible to have a timer that closes connections if not reopened within a timeout?
190 } else {
191 qROWarning(this) << "Request to detach from non-existent RemoteObjectSource:" << m_rxName;
192 }
193 qRODebug(this) << "RemoveObject finished" << m_rxName;
194 break;
195 }
196 case InvokePacket:
197 {
198 int call, index, serialId, propertyId;
199 m_codec->deserializeInvokePacket(in&: connection->d_func()->stream(), call, index, args&: m_rxArgs, serialId, propertyIndex&: propertyId);
200 if (m_rxName == QLatin1String("Registry") && !m_registryMapping.contains(key: connection)) {
201 const QRemoteObjectSourceLocation loc = m_rxArgs.first().value<QRemoteObjectSourceLocation>();
202 m_registryMapping[connection] = loc.second.hostUrl;
203 }
204 if (m_sourceObjects.contains(key: m_rxName)) {
205 QRemoteObjectSourceBase *source = m_sourceObjects[m_rxName];
206 if (call == QMetaObject::InvokeMetaMethod) {
207 const int resolvedIndex = source->m_api->sourceMethodIndex(index);
208 if (resolvedIndex < 0) { //Invalid index
209 qROWarning(this) << "Invalid method invoke packet received. Index =" << index <<"which is out of bounds for type"<<m_rxName;
210 //TODO - consider moving this to packet validation?
211 break;
212 }
213 if (source->m_api->isAdapterMethod(index))
214 qRODebug(this) << "Adapter (method) Invoke-->" << m_rxName << source->m_adapter->metaObject()->method(index: resolvedIndex).name();
215 else {
216 qRODebug(this) << "Source (method) Invoke-->" << m_rxName << source->m_object->metaObject()->method(index: resolvedIndex).methodSignature();
217 auto method = source->m_object->metaObject()->method(index: resolvedIndex);
218 const int parameterCount = method.parameterCount();
219 for (int i = 0; i < parameterCount; i++)
220 m_rxArgs[i] = decodeVariant(value: std::move(m_rxArgs[i]), metaType: method.parameterMetaType(index: i));
221 }
222 auto metaType = QMetaType::fromName(name: source->m_api->typeName(index).constData());
223 if (!metaType.sizeOf())
224 metaType = QMetaType(QMetaType::UnknownType);
225 QVariant returnValue(metaType, nullptr);
226 // If a Replica is used as a Source (which node->proxy() does) we can have a PendingCall return value.
227 // In this case, we need to wait for the pending call and send that.
228 if (source->m_api->typeName(index) == QByteArrayLiteral("QRemoteObjectPendingCall"))
229 returnValue = QVariant::fromValue<QRemoteObjectPendingCall>(value: QRemoteObjectPendingCall());
230 source->invoke(c: QMetaObject::InvokeMetaMethod, index, args: m_rxArgs, returnValue: &returnValue);
231 // send reply if wanted
232 if (serialId >= 0) {
233 if (returnValue.canConvert<QRemoteObjectPendingCall>()) {
234 QRemoteObjectPendingCall call = returnValue.value<QRemoteObjectPendingCall>();
235 // Watcher will be destroyed when connection is, or when the finished lambda is called
236 QRemoteObjectPendingCallWatcher *watcher = new QRemoteObjectPendingCallWatcher(call, connection);
237 QObject::connect(sender: watcher, signal: &QRemoteObjectPendingCallWatcher::finished, context: connection, slot: [this, serialId, connection, watcher]() {
238 if (watcher->error() == QRemoteObjectPendingCall::NoError) {
239 m_codec->serializeInvokeReplyPacket(name: this->m_rxName, ackedSerialId: serialId, value: encodeVariant(value: watcher->returnValue()));
240 m_codec->send(connection);
241 }
242 watcher->deleteLater();
243 });
244 } else {
245 m_codec->serializeInvokeReplyPacket(name: m_rxName, ackedSerialId: serialId, value: encodeVariant(value: returnValue));
246 m_codec->send(connection);
247 }
248 }
249 } else {
250 const int resolvedIndex = source->m_api->sourcePropertyIndex(index);
251 if (resolvedIndex < 0) {
252 qROWarning(this) << "Invalid property invoke packet received. Index =" << index <<"which is out of bounds for type"<<m_rxName;
253 //TODO - consider moving this to packet validation?
254 break;
255 }
256 if (source->m_api->isAdapterProperty(index))
257 qRODebug(this) << "Adapter (write property) Invoke-->" << m_rxName << source->m_adapter->metaObject()->property(index: resolvedIndex).name();
258 else
259 qRODebug(this) << "Source (write property) Invoke-->" << m_rxName << source->m_object->metaObject()->property(index: resolvedIndex).name();
260 source->invoke(c: QMetaObject::WriteProperty, index, args: m_rxArgs);
261 }
262 }
263 break;
264 }
265 default:
266 qRODebug(this) << "OnReadReady invalid type" << packetType;
267 }
268 } while (connection->bytesAvailable()); // have bytes left over, so do another iteration
269}
270
271void QRemoteObjectSourceIo::handleConnection()
272{
273 qRODebug(this) << "handleConnection" << m_connections;
274
275 QtROServerIoDevice *conn = m_server->nextPendingConnection();
276 newConnection(conn);
277}
278
279void QRemoteObjectSourceIo::newConnection(QtROIoDeviceBase *conn)
280{
281 m_connections.insert(value: conn);
282 connect(sender: conn, signal: &QtROIoDeviceBase::readyRead, context: this, slot: [this, conn]() {
283 onServerRead(conn);
284 });
285 connect(sender: conn, signal: &QtROIoDeviceBase::disconnected, context: this, slot: [this, conn]() {
286 onServerDisconnect(conn);
287 });
288
289 m_codec->serializeHandshakePacket();
290 m_codec->send(connection: conn);
291
292 QRemoteObjectPackets::ObjectInfoList infos;
293 infos.reserve(asize: m_sourceRoots.size());
294 for (auto remoteObject : std::as_const(t&: m_sourceRoots)) {
295 infos << QRemoteObjectPackets::ObjectInfo{.name: remoteObject->m_api->name(), .typeName: remoteObject->m_api->typeName(), .signature: remoteObject->m_api->objectSignature()};
296 }
297 m_codec->serializeObjectListPacket(infos);
298 m_codec->send(connection: conn);
299 qRODebug(this) << "Wrote ObjectList packet from Server" << QStringList(m_sourceRoots.keys());
300}
301
302QUrl QRemoteObjectSourceIo::serverAddress() const
303{
304 if (m_server)
305 return m_server->address();
306 return m_address;
307}
308
309QT_END_NAMESPACE
310

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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