1/****************************************************************************
2**
3** Copyright (C) 2017-2015 Ford Motor Company
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtRemoteObjects module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qconnectionfactories_p.h"
41#include "qconnectionfactories_p.h"
42
43// BEGIN: Backends
44#if defined(Q_OS_QNX)
45#include "qconnection_qnx_backend_p.h"
46#endif
47#include "qconnection_local_backend_p.h"
48#include "qconnection_tcpip_backend_p.h"
49// END: Backends
50
51QT_BEGIN_NAMESPACE
52
53using namespace QtRemoteObjects;
54
55class QtROFactoryLoader
56{
57public:
58 QtROClientFactory clientFactory;
59 QtROServerFactory serverFactory;
60};
61
62Q_GLOBAL_STATIC(QtROFactoryLoader, loader)
63
64inline bool fromDataStream(QDataStream &in, QRemoteObjectPacketTypeEnum &type, QString &name)
65{
66 quint16 _type;
67 in >> _type;
68 type = Invalid;
69 switch (_type) {
70 case Handshake: type = Handshake; break;
71 case InitPacket: type = InitPacket; break;
72 case InitDynamicPacket: type = InitDynamicPacket; break;
73 case AddObject: type = AddObject; break;
74 case RemoveObject: type = RemoveObject; break;
75 case InvokePacket: type = InvokePacket; break;
76 case InvokeReplyPacket: type = InvokeReplyPacket; break;
77 case PropertyChangePacket: type = PropertyChangePacket; break;
78 case ObjectList: type = ObjectList; break;
79 case Ping: type = Ping; break;
80 case Pong: type = Pong; break;
81 default:
82 qCWarning(QT_REMOTEOBJECT_IO) << "Invalid packet received" << _type;
83 }
84 if (type == Invalid)
85 return false;
86 if (type == ObjectList)
87 return true;
88 in >> name;
89 qCDebug(QT_REMOTEOBJECT_IO) << "Packet received of type" << type << "for object" << name;
90 return true;
91}
92
93/*!
94 All communication between nodes happens through some form of QIODevice with
95 an associated QDataStream to handle marshalling of Qt types. IoDeviceBase
96 is an abstract base class that provides a consistent interface to QtRO, yet
97 can be extended to support different types of QIODevice.
98 */
99IoDeviceBase::IoDeviceBase(QObject *parent)
100 : QObject(parent), m_isClosing(false), m_curReadSize(0)
101{
102 m_dataStream.setVersion(dataStreamVersion);
103}
104
105IoDeviceBase::~IoDeviceBase()
106{
107}
108
109bool IoDeviceBase::read(QRemoteObjectPacketTypeEnum &type, QString &name)
110{
111 qCDebug(QT_REMOTEOBJECT_IO) << deviceType() << "read()" << m_curReadSize << bytesAvailable();
112
113 if (m_curReadSize == 0) {
114 if (bytesAvailable() < static_cast<int>(sizeof(quint32)))
115 return false;
116
117 m_dataStream >> m_curReadSize;
118 }
119
120 qCDebug(QT_REMOTEOBJECT_IO) << deviceType() << "read()-looking for map" << m_curReadSize << bytesAvailable();
121
122 if (bytesAvailable() < m_curReadSize)
123 return false;
124
125 m_curReadSize = 0;
126 return fromDataStream(m_dataStream, type, name);
127}
128
129void IoDeviceBase::write(const QByteArray &data)
130{
131 if (connection()->isOpen() && !m_isClosing)
132 connection()->write(data);
133}
134
135void IoDeviceBase::write(const QByteArray &data, qint64 size)
136{
137 if (connection()->isOpen() && !m_isClosing)
138 connection()->write(data.data(), size);
139}
140
141void IoDeviceBase::close()
142{
143 m_isClosing = true;
144 doClose();
145}
146
147qint64 IoDeviceBase::bytesAvailable() const
148{
149 return connection()->bytesAvailable();
150}
151
152void IoDeviceBase::initializeDataStream()
153{
154 m_dataStream.setDevice(connection());
155 m_dataStream.resetStatus();
156}
157
158void IoDeviceBase::addSource(const QString &name)
159{
160 m_remoteObjects.insert(name);
161}
162
163void IoDeviceBase::removeSource(const QString &name)
164{
165 m_remoteObjects.remove(name);
166}
167
168QSet<QString> IoDeviceBase::remoteObjects() const
169{
170 return m_remoteObjects;
171}
172
173ClientIoDevice::ClientIoDevice(QObject *parent) : IoDeviceBase(parent)
174{
175}
176
177ClientIoDevice::~ClientIoDevice()
178{
179 if (!m_isClosing)
180 close();
181}
182
183void ClientIoDevice::disconnectFromServer()
184{
185 doDisconnectFromServer();
186 emit shouldReconnect(this);
187}
188
189QUrl ClientIoDevice::url() const
190{
191 return m_url;
192}
193
194QString ClientIoDevice::deviceType() const
195{
196 return QStringLiteral("ClientIoDevice");
197}
198
199/*!
200 The Qt servers create QIODevice derived classes from handleConnection. The
201 problem is that they behave differently, so this class adds some
202 consistency.
203 */
204ServerIoDevice::ServerIoDevice(QObject *parent) : IoDeviceBase(parent)
205{
206}
207
208QString ServerIoDevice::deviceType() const
209{
210 return QStringLiteral("ServerIoDevice");
211}
212
213QConnectionAbstractServer::QConnectionAbstractServer(QObject *parent)
214 : QObject(parent)
215{
216}
217
218QConnectionAbstractServer::~QConnectionAbstractServer()
219{
220}
221
222ServerIoDevice *QConnectionAbstractServer::nextPendingConnection()
223{
224 ServerIoDevice *iodevice = configureNewConnection();
225 iodevice->initializeDataStream();
226 return iodevice;
227}
228
229ExternalIoDevice::ExternalIoDevice(QIODevice *device, QObject *parent)
230 : IoDeviceBase(parent)
231 , m_device(device)
232{
233 initializeDataStream();
234 connect(m_device.data(), &QIODevice::aboutToClose, this, [this]() { this->m_isClosing = true; });
235 connect(m_device.data(), &QIODevice::readyRead, this, &ExternalIoDevice::readyRead);
236 auto meta = device->metaObject();
237 if (-1 == meta->indexOfSignal(SIGNAL(disconnected())))
238 connect(m_device.data(), SIGNAL(disconnected()), this, SIGNAL(disconnected()));
239}
240
241QIODevice *ExternalIoDevice::connection() const
242{
243 return m_device;
244}
245
246bool ExternalIoDevice::isOpen() const
247{
248 if (!m_device)
249 return false;
250 return m_device->isOpen() && IoDeviceBase::isOpen();
251}
252
253void ExternalIoDevice::doClose()
254{
255 if (isOpen())
256 m_device->close();
257}
258
259QString ExternalIoDevice::deviceType() const
260{
261 return QStringLiteral("ExternalIoDevice");
262}
263
264/*!
265 \class QtROServerFactory
266 \inmodule QtRemoteObjects
267 \brief A class that holds information about server backends available on the Qt Remote Objects network.
268*/
269QtROServerFactory::QtROServerFactory()
270{
271#if defined(Q_OS_QNX)
272 registerType<QnxServerImpl>(QStringLiteral("qnx"));
273#endif
274 registerType<LocalServerImpl>(QStringLiteral("local"));
275 registerType<TcpServerImpl>(QStringLiteral("tcp"));
276}
277
278QtROServerFactory *QtROServerFactory::instance()
279{
280 return &loader->serverFactory;
281}
282
283/*!
284 \class QtROClientFactory
285 \inmodule QtRemoteObjects
286 \brief A class that holds information about client backends available on the Qt Remote Objects network.
287*/
288QtROClientFactory::QtROClientFactory()
289{
290#if defined(Q_OS_QNX)
291 registerType<QnxClientIo>(QStringLiteral("qnx"));
292#endif
293 registerType<LocalClientIo>(QStringLiteral("local"));
294 registerType<TcpClientIo>(QStringLiteral("tcp"));
295}
296
297QtROClientFactory *QtROClientFactory::instance()
298{
299 return &loader->clientFactory;
300}
301
302/*!
303 \fn void qRegisterRemoteObjectsClient(const QString &id)
304 \relates QtROClientFactory
305
306 Registers the Remote Objects client \a id for the type \c{T}.
307
308 If you need a custom transport protocol for Qt Remote Objects, you need to
309 register the client & server implementation here.
310
311 \note This function requires that \c{T} is a fully defined type at the point
312 where the function is called.
313
314 This example registers the class \c{CustomClientIo} as \c{"myprotocol"}:
315
316 \code
317 qRegisterRemoteObjectsClient<CustomClientIo>(QStringLiteral("myprotocol"));
318 \endcode
319
320 With this in place, you can now instantiate nodes using this new custom protocol:
321
322 \code
323 QRemoteObjectNode client(QUrl(QStringLiteral("myprotocol:registry")));
324 \endcode
325
326 \sa {qRegisterRemoteObjectsServer}
327*/
328
329/*!
330 \fn void qRegisterRemoteObjectsServer(const QString &id)
331 \relates QtROServerFactory
332
333 Registers the Remote Objects server \a id for the type \c{T}.
334
335 If you need a custom transport protocol for Qt Remote Objects, you need to
336 register the client & server implementation here.
337
338 \note This function requires that \c{T} is a fully defined type at the point
339 where the function is called.
340
341 This example registers the class \c{CustomServerImpl} as \c{"myprotocol"}:
342
343 \code
344 qRegisterRemoteObjectsServer<CustomServerImpl>(QStringLiteral("myprotocol"));
345 \endcode
346
347 With this in place, you can now instantiate nodes using this new custom protocol:
348
349 \code
350 QRemoteObjectNode client(QUrl(QStringLiteral("myprotocol:registry")));
351 \endcode
352
353 \sa {qRegisterRemoteObjectsServer}
354*/
355
356QT_END_NAMESPACE
357