diff options
Diffstat (limited to 'src')
32 files changed, 4908 insertions, 0 deletions
diff --git a/src/remoteobjects/doc/images/README b/src/remoteobjects/doc/images/README new file mode 100644 index 0000000..aec41a2 --- /dev/null +++ b/src/remoteobjects/doc/images/README @@ -0,0 +1 @@ +Put all documentation images into this folder. diff --git a/src/remoteobjects/doc/qtremoteobjects.qdocconf b/src/remoteobjects/doc/qtremoteobjects.qdocconf new file mode 100644 index 0000000..69392af --- /dev/null +++ b/src/remoteobjects/doc/qtremoteobjects.qdocconf @@ -0,0 +1,43 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) + +project = QtRemoteObjects +description = Qt RemoteObjects Reference Documentation +version = $QT_VERSION + +qhp.projects = QtRemoteObjects + +qhp.QtRemoteObjects.file = qtremoteobjects.qhp +qhp.QtRemoteObjects.namespace = org.qt-project.qtremoteobjects.$QT_VERSION_TAG +qhp.QtRemoteObjects.virtualFolder = qtremoteobjects +qhp.QtRemoteObjects.indexTitle = Qt RemoteObjects +qhp.QtRemoteObjects.indexRoot = + +qhp.QtRemoteObjects.filterAttributes = qtremoteobjects $QT_VERSION qtrefdoc +qhp.QtRemoteObjects.customFilters.Qt.name = QtRemoteObjects $QT_VERSION +qhp.QtRemoteObjects.customFilters.Qt.filterAttributes = qtremoteobjects $QT_VERSION +qhp.QtRemoteObjects.subprojects = classes +qhp.QtRemoteObjects.subprojects.classes.title = C++ Classes +qhp.QtRemoteObjects.subprojects.classes.indexTitle = Qt RemoteObjects C++ Classes +qhp.QtRemoteObjects.subprojects.classes.selectors = class fake:headerfile +qhp.QtRemoteObjects.subprojects.classes.sortPages = true + +depends += qtcore \ + qtdoc + +tagfile = ../../../doc/qtremoteobjects/qtremoteobjects.tagsi + +headerdirs += .. \ + ../../remoteobjects + +sourcedirs += .. \ + ../../remoteobjects + +exampledirs += ../../../examples/RemoteObjects \ + snippets/ + +examplesinstallpath = RemoteObjects + +imagedirs += images + +navigation.landingpage = "Qt RemoteObjects" +navigation.cppclassespage = "Qt RemoteObjects C++ Classes" diff --git a/src/remoteobjects/doc/snipplets/README b/src/remoteobjects/doc/snipplets/README new file mode 100644 index 0000000..72ad410 --- /dev/null +++ b/src/remoteobjects/doc/snipplets/README @@ -0,0 +1 @@ +Put all snipplets into this folder. diff --git a/src/remoteobjects/doc/src/qtremoteobjects.qdoc b/src/remoteobjects/doc/src/qtremoteobjects.qdoc new file mode 100644 index 0000000..231ebd2 --- /dev/null +++ b/src/remoteobjects/doc/src/qtremoteobjects.qdoc @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \module QtRemoteObjects + \title Qt RemoteObjects C++ Classes + \ingroup modules + \qtvariable remote objects + + \brief The Qt RemoteObjects module provides functionality for remoting QObjects. + +*/ diff --git a/src/remoteobjects/qconnectionabstractfactory_p.h b/src/remoteobjects/qconnectionabstractfactory_p.h new file mode 100644 index 0000000..b681695 --- /dev/null +++ b/src/remoteobjects/qconnectionabstractfactory_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONNECTIONABSTRACTFACTORY_P_H +#define QCONNECTIONABSTRACTFACTORY_P_H + +#include <QHash> + +QT_BEGIN_NAMESPACE + +class QObject; + +template <class OutputType, class IndexType = QString> +class QConnectionAbstractFactory +{ + Q_DISABLE_COPY(QConnectionAbstractFactory) + +public: + QConnectionAbstractFactory() {} + + template<class T> + void registerProduct(const IndexType &scheme) + { + m_mapping[scheme] = &createInstance<T>; + } + + OutputType *create(const IndexType &type, QObject *parent = Q_NULLPTR) const + { + typename QHash<IndexType, FactoryFunction>::const_iterator res = m_mapping.find(type); + if (res != m_mapping.end()) + return (*res)(parent); + else + return Q_NULLPTR; + } + +private: + typedef OutputType* (*FactoryFunction)(QObject*); + QHash<IndexType, FactoryFunction> m_mapping; + + template<class Type> + static OutputType* createInstance(QObject *parent) + { + return new Type(parent); + } +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qconnectionabstractserver.cpp b/src/remoteobjects/qconnectionabstractserver.cpp new file mode 100644 index 0000000..672bd2e --- /dev/null +++ b/src/remoteobjects/qconnectionabstractserver.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qconnectionabstractserver_p.h" +#include "qtremoteobjectglobal.h" + +QT_BEGIN_NAMESPACE + +ServerIoDevice::ServerIoDevice(QObject *parent) + : QObject(parent), m_isClosing(false), m_curReadSize(0), m_packet(Q_NULLPTR) +{ +} + +ServerIoDevice::~ServerIoDevice() +{ + if (m_packet) + delete m_packet; +} + +bool ServerIoDevice::read() +{ + qCDebug(QT_REMOTEOBJECT) << "ServerIODevice::read()" << m_curReadSize << bytesAvailable(); + + QDataStream in(connection()); + if (m_curReadSize == 0) { + if (bytesAvailable() < static_cast<int>(sizeof(quint32))) + return false; + + in >> m_curReadSize; + } + + qCDebug(QT_REMOTEOBJECT) << "ServerIODevice::read()-looking for map" << m_curReadSize << bytesAvailable(); + + if (bytesAvailable() < m_curReadSize) + return false; + + m_curReadSize = 0; + if (m_packet) + delete m_packet; + m_packet = QRemoteObjectPackets::QRemoteObjectPacket::fromDataStream(in); + return m_packet && m_packet->id != QRemoteObjectPackets::QRemoteObjectPacket::Invalid; +} + +void ServerIoDevice::close() +{ + m_isClosing = true; + doClose(); +} + +void ServerIoDevice::write(const QByteArray &data) +{ + if (connection()->isOpen() && !m_isClosing) + connection()->write(data); +} + +qint64 ServerIoDevice::bytesAvailable() +{ + return connection()->bytesAvailable(); +} + +QRemoteObjectPackets::QRemoteObjectPacket *ServerIoDevice::packet() const +{ + return m_packet; +} + + +QConnectionAbstractServer::QConnectionAbstractServer(QObject *parent) + : QObject(parent) +{ +} + +QConnectionAbstractServer::~QConnectionAbstractServer() +{ +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qconnectionabstractserver_p.h b/src/remoteobjects/qconnectionabstractserver_p.h new file mode 100644 index 0000000..4d55f56 --- /dev/null +++ b/src/remoteobjects/qconnectionabstractserver_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONNECTIONABSTRACTSERVER_P_H +#define QCONNECTIONABSTRACTSERVER_P_H + +#include <QAbstractSocket> +#include <QDataStream> +#include <QIODevice> +#include <QLocalSocket> +#include <QObject> +#include <QTcpSocket> +#include <QVariant> +#include "qtremoteobjectglobal.h" + +QT_BEGIN_NAMESPACE + +//The Qt servers create QIODevice derived classes from handleConnection. +//The problem is that they behave differently, so this class adds some +//consistency. +class ServerIoDevice : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(ServerIoDevice) + +public: + explicit ServerIoDevice(QObject *parent = Q_NULLPTR); + virtual ~ServerIoDevice(); + + bool read(); + + virtual void write(const QByteArray &data); + void close(); + virtual qint64 bytesAvailable(); + QRemoteObjectPackets::QRemoteObjectPacket *packet() const; + virtual QIODevice *connection() const = 0; + +Q_SIGNALS: + void disconnected(); + void readyRead(); + +protected: + virtual void doClose() = 0; + +private: + bool m_isClosing; + quint32 m_curReadSize; + QRemoteObjectPackets::QRemoteObjectPacket *m_packet; +}; + + +class QConnectionAbstractServer : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(QConnectionAbstractServer) + +public: + explicit QConnectionAbstractServer(QObject *parent = Q_NULLPTR); + virtual ~QConnectionAbstractServer(); + + virtual bool hasPendingConnections() const = 0; + virtual ServerIoDevice* nextPendingConnection() = 0; + virtual QUrl address() const = 0; + virtual bool listen(const QUrl &address) = 0; + virtual QAbstractSocket::SocketError serverError() const = 0; + virtual void close() = 0; + +Q_SIGNALS: + void newConnection(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qconnectionclientfactory.cpp b/src/remoteobjects/qconnectionclientfactory.cpp new file mode 100644 index 0000000..23e1527 --- /dev/null +++ b/src/remoteobjects/qconnectionclientfactory.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qconnectionclientfactory_p.h" +#include "qtremoteobjectglobal.h" + +#include <QDataStream> +#include <QHostAddress> +#include <QHostInfo> + +QT_BEGIN_NAMESPACE + +ClientIoDevice::ClientIoDevice(QObject *parent) + : QObject(parent), m_isClosing(false), m_curReadSize(0), m_packet(Q_NULLPTR) +{ +} + +ClientIoDevice::~ClientIoDevice() +{ + if (m_packet) + delete m_packet; +} + +void ClientIoDevice::close() +{ + m_isClosing = true; + doClose(); +} + +bool ClientIoDevice::read() +{ + qCDebug(QT_REMOTEOBJECT) << "ClientIODevice::read()" << m_curReadSize << bytesAvailable(); + + QDataStream in(connection()); + if (m_curReadSize == 0) { + if (bytesAvailable() < static_cast<int>(sizeof(quint32))) + return false; + + in >> m_curReadSize; + } + + qCDebug(QT_REMOTEOBJECT) << "ClientIODevice::read()-looking for map" << m_curReadSize << bytesAvailable(); + + if (bytesAvailable() < m_curReadSize) + return false; + + m_curReadSize = 0; + if (m_packet) + delete m_packet; + m_packet = QRemoteObjectPackets::QRemoteObjectPacket::fromDataStream(in); + return m_packet && m_packet->id != QRemoteObjectPackets::QRemoteObjectPacket::Invalid; +} + +void ClientIoDevice::write(const QByteArray &data) +{ + connection()->write(data); +} + +qint64 ClientIoDevice::bytesAvailable() +{ + return connection()->bytesAvailable(); +} + +QRemoteObjectPackets::QRemoteObjectPacket *ClientIoDevice::packet() const +{ + return m_packet; +} + +QUrl ClientIoDevice::url() const +{ + return m_url; +} + +void ClientIoDevice::addSource(const QString &name) +{ + m_remoteObjects.insert(name); +} + +void ClientIoDevice::removeSource(const QString &name) +{ + m_remoteObjects.remove(name); +} + +QSet<QString> ClientIoDevice::remoteObjects() const +{ + return m_remoteObjects; +} + + +LocalClientIo::LocalClientIo(QObject *parent) + : ClientIoDevice(parent) +{ + connect(&m_socket, SIGNAL(readyRead()), this, SIGNAL(readyRead())); + connect(&m_socket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(onError(QLocalSocket::LocalSocketError))); + connect(&m_socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)), this, SLOT(onStateChanged(QLocalSocket::LocalSocketState))); +} + +LocalClientIo::~LocalClientIo() +{ + close(); +} + +QIODevice *LocalClientIo::connection() +{ + return &m_socket; +} + +void LocalClientIo::doClose() +{ + if (m_socket.isOpen()) { + connect(&m_socket, SIGNAL(disconnected()), this, SLOT(deleteLater())); + m_socket.disconnectFromServer(); + } else { + this->deleteLater(); + } +} + +void LocalClientIo::connectToServer() +{ + if (!isOpen()) + m_socket.connectToServer(url().path()); +} + +bool LocalClientIo::isOpen() +{ + return !isClosing() && m_socket.isOpen(); +} + +void LocalClientIo::onError(QLocalSocket::LocalSocketError error) +{ + qCDebug(QT_REMOTEOBJECT) << "onError" << error; + + switch (error) { + case QLocalSocket::ServerNotFoundError: //Host not there, wait and try again + emit shouldReconnect(this); + break; + case QLocalSocket::ConnectionError: + case QLocalSocket::ConnectionRefusedError: + //... TODO error reporting + break; + default: + break; + } +} + +void LocalClientIo::onStateChanged(QLocalSocket::LocalSocketState state) +{ + if (state == QLocalSocket::ClosingState && !isClosing()) { + m_socket.abort(); + emit shouldReconnect(this); + } +} + + +TcpClientIo::TcpClientIo(QObject *parent) + : ClientIoDevice(parent) +{ + connect(&m_socket, SIGNAL(readyRead()), this, SIGNAL(readyRead())); + connect(&m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError))); + connect(&m_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState))); +} + +TcpClientIo::~TcpClientIo() +{ + close(); +} + +QIODevice *TcpClientIo::connection() +{ + return &m_socket; +} + +void TcpClientIo::doClose() +{ + if (m_socket.isOpen()) { + connect(&m_socket, SIGNAL(disconnected()), this, SLOT(deleteLater())); + m_socket.disconnectFromHost(); + } else { + this->deleteLater(); + } +} + +void TcpClientIo::connectToServer() +{ + if (isOpen()) + return; + QHostAddress address(url().host()); + if (address.isNull()) { + const QList<QHostAddress> addresses = QHostInfo::fromName(url().host()).addresses(); + Q_ASSERT_X(addresses.size() >= 1, Q_FUNC_INFO, url().toString().toLatin1().data()); + address = addresses.first(); + } + + m_socket.connectToHost(address, url().port()); +} + +bool TcpClientIo::isOpen() +{ + return (!isClosing() && m_socket.isOpen()); +} + +void TcpClientIo::onError(QAbstractSocket::SocketError error) +{ + qCDebug(QT_REMOTEOBJECT) << "onError" << error; + + switch (error) { + case QAbstractSocket::HostNotFoundError: //Host not there, wait and try again + emit shouldReconnect(this); + break; + case QAbstractSocket::AddressInUseError: + case QAbstractSocket::ConnectionRefusedError: + //... TODO error reporting + break; + default: + break; + } +} + +void TcpClientIo::onStateChanged(QAbstractSocket::SocketState state) +{ + if (state == QAbstractSocket::ClosingState && !isClosing()) { + m_socket.abort(); + emit shouldReconnect(this); + } +} + + +QConnectionClientFactory::QConnectionClientFactory() +{ + registerProduct<LocalClientIo>(QRemoteObjectStringLiterals::local()); + registerProduct<TcpClientIo>(QRemoteObjectStringLiterals::tcp()); +} + +ClientIoDevice *QConnectionClientFactory::createDevice(const QUrl &url, QObject *parent) +{ + ClientIoDevice *res = QConnectionAbstractFactory::create(url.scheme(), parent); + res->m_url = url; + return res; +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qconnectionclientfactory_p.h b/src/remoteobjects/qconnectionclientfactory_p.h new file mode 100644 index 0000000..5fa0cce --- /dev/null +++ b/src/remoteobjects/qconnectionclientfactory_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONNECTIONCLIENTFACTORY_P_H +#define QCONNECTIONCLIENTFACTORY_P_H + +#include "qconnectionabstractfactory_p.h" +#include "qtremoteobjectglobal.h" + +#include <QLocalSocket> +#include <QObject> +#include <QTcpSocket> +#include <QUrl> +#include <QVariant> + +QT_BEGIN_NAMESPACE + +class ClientIoDevice : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(ClientIoDevice) + +public: + explicit ClientIoDevice(QObject *parent = Q_NULLPTR); + virtual ~ClientIoDevice(); + + bool read(); + virtual void write(const QByteArray &data); + void close(); + virtual void connectToServer() = 0; + virtual qint64 bytesAvailable(); + + QRemoteObjectPackets::QRemoteObjectPacket *packet() const; + QUrl url() const; + void addSource(const QString &); + void removeSource(const QString &); + QSet<QString> remoteObjects() const; + + virtual bool isOpen() = 0; + virtual QIODevice *connection() = 0; + +Q_SIGNALS: + void disconnected(); + void readyRead(); + void shouldReconnect(ClientIoDevice*); +protected: + virtual void doClose() = 0; + inline bool isClosing(); + +private: + bool m_isClosing; + QUrl m_url; + +private: + friend class QConnectionClientFactory; + + quint32 m_curReadSize; + QRemoteObjectPackets::QRemoteObjectPacket* m_packet; + QSet<QString> m_remoteObjects; +}; + +bool ClientIoDevice::isClosing() +{ + return m_isClosing; +} + +class LocalClientIo : public ClientIoDevice +{ + Q_OBJECT + +public: + explicit LocalClientIo(QObject *parent = Q_NULLPTR); + ~LocalClientIo(); + + QIODevice *connection() Q_DECL_OVERRIDE; + void connectToServer() Q_DECL_OVERRIDE; + bool isOpen() Q_DECL_OVERRIDE; + +public Q_SLOTS: + void onError(QLocalSocket::LocalSocketError error); + void onStateChanged(QLocalSocket::LocalSocketState state); + +protected: + void doClose() Q_DECL_OVERRIDE; +private: + QLocalSocket m_socket; +}; + + +class TcpClientIo : public ClientIoDevice +{ + Q_OBJECT + +public: + explicit TcpClientIo(QObject *parent = Q_NULLPTR); + ~TcpClientIo(); + + QIODevice *connection() Q_DECL_OVERRIDE; + void connectToServer() Q_DECL_OVERRIDE; + bool isOpen() Q_DECL_OVERRIDE; + +public Q_SLOTS: + void onError(QAbstractSocket::SocketError error); + void onStateChanged(QAbstractSocket::SocketState state); + +protected: + void doClose() Q_DECL_OVERRIDE; + +private: + QTcpSocket m_socket; +}; + +class QConnectionClientFactory : public QConnectionAbstractFactory<ClientIoDevice> +{ + Q_DISABLE_COPY(QConnectionClientFactory) + +public: + QConnectionClientFactory(); + + ClientIoDevice *createDevice(const QUrl &url, QObject *parent = Q_NULLPTR); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qconnectionserverfactory.cpp b/src/remoteobjects/qconnectionserverfactory.cpp new file mode 100644 index 0000000..f211ee7 --- /dev/null +++ b/src/remoteobjects/qconnectionserverfactory.cpp @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qconnectionserverfactory_p.h" + +#include <qcompilerdetection.h> +#include <QHostInfo> +#include <QIODevice> +#include <QLocalServer> +#include <QTcpServer> +#include <QtGlobal> + +#ifdef Q_OS_LINUX +#include <QFile> +#include <QDir> +#endif + +QT_BEGIN_NAMESPACE + +LocalServerIo::LocalServerIo(QLocalSocket *conn, QObject *parent) + : ServerIoDevice(parent), m_connection(conn) +{ + m_connection->setParent(this); + connect(conn, SIGNAL(readyRead()), this, SIGNAL(readyRead())); + connect(conn, SIGNAL(disconnected()), this, SIGNAL(disconnected())); +} + +QIODevice *LocalServerIo::connection() const +{ + return m_connection; +} + +void LocalServerIo::doClose() +{ + m_connection->disconnectFromServer(); +} + + +TcpServerIo::TcpServerIo(QTcpSocket *conn, QObject *parent) + : ServerIoDevice(parent), m_connection(conn) +{ + m_connection->setParent(this); + connect(conn, SIGNAL(readyRead()), this, SIGNAL(readyRead())); + connect(conn, SIGNAL(disconnected()), this, SIGNAL(disconnected())); +} + +QIODevice *TcpServerIo::connection() const +{ + return m_connection; +} + +void TcpServerIo::doClose() +{ + m_connection->disconnectFromHost(); +} + + +LocalServerImpl::LocalServerImpl(QObject *parent) + : QConnectionAbstractServer(parent) +{ + connect(&m_server, SIGNAL(newConnection()), this, SIGNAL(newConnection())); +} + +LocalServerImpl::~LocalServerImpl() +{ + m_server.close(); +} + +ServerIoDevice *LocalServerImpl::nextPendingConnection() +{ + if (!m_server.isListening()) + return Q_NULLPTR; + + return new LocalServerIo(m_server.nextPendingConnection(), this); +} + +bool LocalServerImpl::hasPendingConnections() const +{ + return m_server.hasPendingConnections(); +} + +QUrl LocalServerImpl::address() const +{ + QUrl result; + result.setPath(m_server.serverName()); + result.setScheme(QRemoteObjectStringLiterals::local()); + + return result; +} + +bool LocalServerImpl::listen(const QUrl &address) +{ +#ifdef Q_OS_LINUX + QFile socketFile(QDir::tempPath() + QDir::separator() + address.path()); + socketFile.remove(); +#endif + return m_server.listen(address.path()); +} + +QAbstractSocket::SocketError LocalServerImpl::serverError() const +{ + return m_server.serverError(); +} + +void LocalServerImpl::close() +{ + close(); +} + + +TcpServerImpl::TcpServerImpl(QObject *parent) + : QConnectionAbstractServer(parent) +{ + connect(&m_server, SIGNAL(newConnection()), this, SIGNAL(newConnection())); +} + +TcpServerImpl::~TcpServerImpl() +{ + close(); +} + +ServerIoDevice *TcpServerImpl::nextPendingConnection() +{ + if (!m_server.isListening()) + return Q_NULLPTR; + + return new TcpServerIo(m_server.nextPendingConnection()); +} + +bool TcpServerImpl::hasPendingConnections() const +{ + return m_server.hasPendingConnections(); +} + +QUrl TcpServerImpl::address() const +{ + return m_originalUrl; +} + +bool TcpServerImpl::listen(const QUrl &address) +{ + QHostAddress host(address.host()); + if (host.isNull()) { + const QList<QHostAddress> addresses = QHostInfo::fromName(address.host()).addresses();; + Q_ASSERT(addresses.size() >= 1); + host = addresses.first(); + m_originalUrl = address; + } + + return m_server.listen(host, address.port()); +} + +QAbstractSocket::SocketError TcpServerImpl::serverError() const +{ + return m_server.serverError(); +} + +void TcpServerImpl::close() +{ + m_server.close(); +} + +QConnectionServerFactory::QConnectionServerFactory() +{ + registerProduct<LocalServerImpl>(QRemoteObjectStringLiterals::local()); + registerProduct<TcpServerImpl>(QRemoteObjectStringLiterals::tcp()); +} + +QConnectionAbstractServer *QConnectionServerFactory::createServer(const QUrl &url, QObject *parent) +{ + return create(url, parent); +} + +QConnectionAbstractServer *QConnectionServerFactory::create(const QUrl &url, QObject *parent) +{ + return QConnectionAbstractFactory::create(url.scheme(), parent); +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qconnectionserverfactory_p.h b/src/remoteobjects/qconnectionserverfactory_p.h new file mode 100644 index 0000000..4d4638a --- /dev/null +++ b/src/remoteobjects/qconnectionserverfactory_p.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONNECTIONSERVERFACTORY_P_H +#define QCONNECTIONSERVERFACTORY_P_H + +#include "qconnectionabstractfactory_p.h" +#include "qconnectionabstractserver_p.h" + +#include <QLocalServer> +#include <QTcpServer> +#include <QUrl> + +QT_BEGIN_NAMESPACE + +class LocalServerIo : public ServerIoDevice +{ +public: + explicit LocalServerIo(QLocalSocket *conn, QObject *parent = Q_NULLPTR); + + QIODevice *connection() const Q_DECL_OVERRIDE; +protected: + void doClose() Q_DECL_OVERRIDE; + +private: + QLocalSocket *m_connection; +}; + + +class TcpServerIo : public ServerIoDevice +{ +public: + explicit TcpServerIo(QTcpSocket *conn, QObject *parent = Q_NULLPTR); + + QIODevice *connection() const Q_DECL_OVERRIDE; +protected: + void doClose() Q_DECL_OVERRIDE; + +private: + QTcpSocket *m_connection; +}; + + +class LocalServerImpl : public QConnectionAbstractServer +{ + Q_OBJECT + Q_DISABLE_COPY(LocalServerImpl) + +public: + explicit LocalServerImpl(QObject *parent); + ~LocalServerImpl(); + + bool hasPendingConnections() const Q_DECL_OVERRIDE; + ServerIoDevice *nextPendingConnection() Q_DECL_OVERRIDE; + QUrl address() const Q_DECL_OVERRIDE; + bool listen(const QUrl &address) Q_DECL_OVERRIDE; + QAbstractSocket::SocketError serverError() const Q_DECL_OVERRIDE; + void close() Q_DECL_OVERRIDE; + +private: + QLocalServer m_server; +}; + + +class TcpServerImpl : public QConnectionAbstractServer +{ + Q_OBJECT + Q_DISABLE_COPY(TcpServerImpl) + +public: + explicit TcpServerImpl(QObject *parent); + ~TcpServerImpl(); + + bool hasPendingConnections() const Q_DECL_OVERRIDE; + ServerIoDevice *nextPendingConnection() Q_DECL_OVERRIDE; + QUrl address() const Q_DECL_OVERRIDE; + bool listen(const QUrl &address) Q_DECL_OVERRIDE; + QAbstractSocket::SocketError serverError() const Q_DECL_OVERRIDE; + void close() Q_DECL_OVERRIDE; + +private: + QTcpServer m_server; + QUrl m_originalUrl; // necessary because of a QHostAddress bug +}; + + +class QConnectionServerFactory : public QConnectionAbstractFactory<QConnectionAbstractServer> +{ + Q_DISABLE_COPY(QConnectionServerFactory) + +public: + QConnectionServerFactory(); + QConnectionAbstractServer *createServer(const QUrl &url, QObject *parent = Q_NULLPTR); + static void registerScheme(const QString &scheme); + +private: + QConnectionAbstractServer *create(const QUrl &url, QObject *parent); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qregistrysource.cpp b/src/remoteobjects/qregistrysource.cpp new file mode 100644 index 0000000..95fa12e --- /dev/null +++ b/src/remoteobjects/qregistrysource.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qregistrysource_p.h" + +QT_BEGIN_NAMESPACE + +QRegistrySource::QRegistrySource(QObject *parent) + : QRemoteObjectSource(parent) +{ + qRegisterMetaTypeStreamOperators<QRemoteObjectSourceLocation>(); + qRegisterMetaTypeStreamOperators<QRemoteObjectSourceLocations>(); +} + +QRegistrySource::~QRegistrySource() +{ +} + +QRemoteObjectSourceLocations QRegistrySource::sourceLocations() const +{ + qCDebug(QT_REMOTEOBJECT) << "sourceLocations property requested on RegistrySource" << m_sourceLocations; + return m_sourceLocations; +} + +void QRegistrySource::removeServer(const QUrl &url) +{ + QVector<QString> results; + typedef QRemoteObjectSourceLocations::const_iterator CustomIterator; + const CustomIterator end = m_sourceLocations.constEnd(); + for (CustomIterator it = m_sourceLocations.constBegin(); it != end; ++it) + if (it.value() == url) + results.push_back(it.key()); + Q_FOREACH (const QString &res, results) + m_sourceLocations.remove(res); +} + +void QRegistrySource::addSource(const QRemoteObjectSourceLocation &entry) +{ + qCDebug(QT_REMOTEOBJECT) << "An entry was added to the RegistrySource" << entry; + if (!m_sourceLocations.contains(entry.first)) { + m_sourceLocations[entry.first] = entry.second; + emit remoteObjectAdded(entry); + } +} + +void QRegistrySource::removeSource(const QRemoteObjectSourceLocation &entry) +{ + if (m_sourceLocations.contains(entry.first) && m_sourceLocations[entry.first] == entry.second) { + m_sourceLocations.remove(entry.first); + emit remoteObjectRemoved(entry); + } +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qregistrysource_p.h b/src/remoteobjects/qregistrysource_p.h new file mode 100644 index 0000000..f44b6d2 --- /dev/null +++ b/src/remoteobjects/qregistrysource_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREGISTRYSOURCE_P_H +#define QREGISTRYSOURCE_P_H + +#include "qremoteobjectsource.h" + +QT_BEGIN_NAMESPACE + +class QRegistrySource : public QRemoteObjectSource +{ + Q_OBJECT + Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "Registry") + + Q_PROPERTY(QRemoteObjectSourceLocations sourceLocations READ sourceLocations) + +public: + explicit QRegistrySource(QObject *parent = Q_NULLPTR); + ~QRegistrySource(); + + QRemoteObjectSourceLocations sourceLocations() const; + +Q_SIGNALS: + void remoteObjectAdded(const QRemoteObjectSourceLocation &entry); + void remoteObjectRemoved(const QRemoteObjectSourceLocation &entry); + +public Q_SLOTS: + void addSource(const QRemoteObjectSourceLocation &entry); + void removeSource(const QRemoteObjectSourceLocation &entry); + void removeServer(const QUrl &url); + +private: + QRemoteObjectSourceLocations m_sourceLocations; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectdynamicreplica.cpp b/src/remoteobjects/qremoteobjectdynamicreplica.cpp new file mode 100644 index 0000000..eb8aaac --- /dev/null +++ b/src/remoteobjects/qremoteobjectdynamicreplica.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteobjectdynamicreplica.h" +#include "qremoteobjectreplica_p.h" + +#include <QDebug> +#include <QMetaProperty> + +QT_BEGIN_NAMESPACE + +QRemoteObjectDynamicReplica::QRemoteObjectDynamicReplica(QObject *parent) + : QRemoteObjectReplica(parent) +{ +} + +QRemoteObjectDynamicReplica::~QRemoteObjectDynamicReplica() +{ +} + +const QMetaObject* QRemoteObjectDynamicReplica::metaObject() const +{ + Q_D(const QRemoteObjectReplica); + + return d->m_metaObject ? d->m_metaObject : QRemoteObjectReplica::metaObject(); +} + +void *QRemoteObjectDynamicReplica::qt_metacast(const char *name) +{ + Q_D(QRemoteObjectReplica); + + if (!name) + return 0; + + if (!strcmp(name, "QRemoteObjectDynamicReplica")) + return static_cast<void*>(const_cast<QRemoteObjectDynamicReplica*>(this)); + + // not entirely sure that one is needed... TODO: check + if (QString::fromLatin1(name) == d->m_objectName) + return static_cast<void*>(const_cast<QRemoteObjectDynamicReplica*>(this)); + + return QObject::qt_metacast(name); +} + +int QRemoteObjectDynamicReplica::qt_metacall(QMetaObject::Call call, int id, void **argv) +{ + Q_D(QRemoteObjectReplica); + + int saved_id = id; + id = QRemoteObjectReplica::qt_metacall(call, id, argv); + if (id < 0 || d->m_metaObject == Q_NULLPTR) + return id; + + if (call == QMetaObject::ReadProperty || call == QMetaObject::WriteProperty) { + QMetaProperty mp = metaObject()->property(saved_id); + int &status = *reinterpret_cast<int *>(argv[2]); + + if (call == QMetaObject::WriteProperty) { + QVariantList args; + args << QVariant(mp.userType(), argv[0]); + QRemoteObjectReplica::send(QMetaObject::WriteProperty, saved_id, args); + } else { + const QVariant value = propAsVariant(id); + QMetaType::construct(mp.userType(), argv[0], value.data()); + const bool readStatus = true; + // Caller supports QVariant returns? Then we can also report errors + // by storing an invalid variant. + if (!readStatus && argv[1]) { + status = 0; + reinterpret_cast<QVariant*>(argv[1])->clear(); + } + } + + id = -1; + } else if (call == QMetaObject::InvokeMetaMethod) { + const int methodType = d->m_remoteObjectMethodTypes.at(id); + + if (methodType == QMetaMethod::Signal) { + // signal relay from Source world to Replica + QMetaObject::activate(this, d->m_metaObject, id, argv); + + } else if (methodType == QMetaMethod::Slot || methodType == QMetaMethod::Method) { + // method relay from Replica to Source + qCDebug(QT_REMOTEOBJECT) << "method" << d->m_metaObject->method(saved_id).name() << "invoked"; + + QVariantList args; + for (int i = 1; i <= d->m_methodArgumentTypes.at(id).size(); ++i) { + args << QVariant(d->m_methodArgumentTypes.at(id)[i-1], argv[i]); + } + + QRemoteObjectReplica::send(QMetaObject::InvokeMetaMethod, saved_id, args); + } + } + + return id; +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectdynamicreplica.h b/src/remoteobjects/qremoteobjectdynamicreplica.h new file mode 100644 index 0000000..e676468 --- /dev/null +++ b/src/remoteobjects/qremoteobjectdynamicreplica.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDYNAMICREMOTEOBJECT_H +#define QDYNAMICREMOTEOBJECT_H + +#include "qremoteobjectreplica.h" + +QT_BEGIN_NAMESPACE + +class Q_REMOTEOBJECTS_EXPORT QRemoteObjectDynamicReplica : public QRemoteObjectReplica +{ +public: + ~QRemoteObjectDynamicReplica(); + + const QMetaObject *metaObject() const Q_DECL_OVERRIDE; + void *qt_metacast(const char *name) Q_DECL_OVERRIDE; + int qt_metacall(QMetaObject::Call call, int id, void **argv) Q_DECL_OVERRIDE; + +private: + explicit QRemoteObjectDynamicReplica(QObject *parent = Q_NULLPTR); + friend class QRemoteObjectNodePrivate; + friend class QRemoteObjectNode; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectnode.cpp b/src/remoteobjects/qremoteobjectnode.cpp new file mode 100644 index 0000000..5e4924d --- /dev/null +++ b/src/remoteobjects/qremoteobjectnode.cpp @@ -0,0 +1,690 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteobjectnode.h" +#include "qremoteobjectnode_p.h" + +#include "qremoteobjectregistry.h" +#include "qremoteobjectdynamicreplica.h" +#include "qregistrysource_p.h" +#include "qremoteobjectreplica_p.h" +#include "qremoteobjectsource_p.h" + +#include <QMetaProperty> + +QT_BEGIN_NAMESPACE + +static QString name(const QMetaObject * const mobj) +{ + const int ind = mobj->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE); + return ind >= 0 ? QString::fromLatin1(mobj->classInfo(ind).value()) : QString(); +} + +template <typename K, typename V, typename Query> +bool map_contains(const QMap<K,V> &map, const Query &key, typename QMap<K,V>::const_iterator &result) +{ + const typename QMap<K,V>::const_iterator it = map.find(key); + if (it == map.end()) + return false; + result = it; + return true; +} + +QRemoteObjectNodePrivate::QRemoteObjectNodePrivate() + : QObject(Q_NULLPTR) + , retryInterval(250) + , m_lastError(QRemoteObjectNode::NoError) +{ + connect(&clientRead, SIGNAL(mapped(QObject*)), this, SLOT(onClientRead(QObject*))); +} + +QRemoteObjectNodePrivate::~QRemoteObjectNodePrivate() +{ + Q_FOREACH (ClientIoDevice *conn, knownNodes) { + conn->close(); + conn->deleteLater(); + } +} + +QRemoteObjectSourceLocations QRemoteObjectNodePrivate::remoteObjectAddresses() const +{ + if (!registrySource.isNull()) + return registrySource->sourceLocations(); + else if (!registry.isNull()) + return registry->sourceLocations(); + return QRemoteObjectSourceLocations(); +} + +void QRemoteObjectNodePrivate::timerEvent(QTimerEvent*) +{ + Q_FOREACH (ClientIoDevice *conn, pendingReconnect) { + if (conn->isOpen()) + pendingReconnect.remove(conn); + else + conn->connectToServer(); + } + + if (pendingReconnect.isEmpty()) + reconnectTimer.stop(); + + qCDebug(QT_REMOTEOBJECT) << "timerEvent" << pendingReconnect.size(); +} + +QRemoteObjectReplica *QRemoteObjectNodePrivate::acquire(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name) +{ + qCDebug(QT_REMOTEOBJECT) << "Starting acquire for" << name; + isInitialized.storeRelease(1); + openConnectionIfNeeded(name); + QMutexLocker locker(&mutex); + if (hasInstance(name)) { + qCDebug(QT_REMOTEOBJECT)<<"Acquire - using existing instance"; + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(name).toStrongRef(); + Q_ASSERT(rep); + instance->d_ptr = rep; + rep->configurePrivate(instance); + } else { + QMap<QString, QRemoteObjectSourcePrivate*>::const_iterator mapIt; + if (!remoteObjectIo.isNull() && map_contains(remoteObjectIo->m_remoteObjects, name, mapIt)) { + QInProcessReplicaPrivate *rp = new QInProcessReplicaPrivate(name, meta); + instance->d_ptr.reset(rp); + rp->configurePrivate(instance); + connectReplica(mapIt.value()->m_object, instance); + rp->connectionToSource = mapIt.value(); + } else { + QConnectedReplicaPrivate *rp = new QConnectedReplicaPrivate(name, meta); + instance->d_ptr.reset(rp); + rp->configurePrivate(instance); + if (connectedSources.contains(name)) { //Either we have a peer connections, or existing connection via registry + rp->setConnection(connectedSources[name]); + } else if (remoteObjectAddresses().contains(name)) { //No existing connection, but we know we can connect via registry + initConnection(remoteObjectAddresses()[name]); //This will try the connection, and if successful, the remoteObjects will be sent + //The link to the replica will be handled then + } + } + instance->initialize(); + replicas.insert(name, instance->d_ptr.toWeakRef()); + qCDebug(QT_REMOTEOBJECT) << "Acquire - Created new instance" << name<<remoteObjectAddresses(); + } + return instance; +} + +const QRemoteObjectRegistry *QRemoteObjectNode::registry() const +{ + return d_ptr->registry.data(); +} + +void QRemoteObjectNodePrivate::connectReplica(QObject *object, QRemoteObjectReplica *instance) +{ + int nConnections = 0; + const QMetaObject *us = instance->metaObject(); + const QMetaObject *them = object->metaObject(); + + static const int memberOffset = QRemoteObjectReplica::staticMetaObject.methodCount(); + for (int idx = memberOffset; idx < us->methodCount(); ++idx) { + const QMetaMethod mm = us->method(idx); + + qCDebug(QT_REMOTEOBJECT) << idx << mm.name(); + if (mm.methodType() != QMetaMethod::Signal) + continue; + + // try to connect to a signal on the parent that has the same method signature + QByteArray sig = QMetaObject::normalizedSignature(mm.methodSignature().constData()); + qCDebug(QT_REMOTEOBJECT) << sig; + if (them->indexOfSignal(sig.constData()) == -1) + continue; + + sig.prepend(QSIGNAL_CODE + '0'); + const char * const csig = sig.constData(); + const bool res = QObject::connect(object, csig, instance, csig); + Q_UNUSED(res); + ++nConnections; + + qCDebug(QT_REMOTEOBJECT) << sig << res; + } + + qCDebug(QT_REMOTEOBJECT) << "# connections =" << nConnections; +} + +void QRemoteObjectNodePrivate::openConnectionIfNeeded(const QString &name) +{ + qCDebug(QT_REMOTEOBJECT) << Q_FUNC_INFO << name << this; + if (remoteObjectAddresses().contains(name)) { + initConnection(remoteObjectAddresses()[name]); + qCDebug(QT_REMOTEOBJECT) << "openedConnection" << remoteObjectAddresses()[name]; + } +} + +void QRemoteObjectNodePrivate::initConnection(const QUrl &address) +{ + if (requestedUrls.contains(address)) { + qCWarning(QT_REMOTEOBJECT) << "Connection already initialized for " << address.toString(); + return; + } + + requestedUrls.insert(address); + + ClientIoDevice *connection = m_factory.createDevice(address, this); + Q_ASSERT_X(connection, Q_FUNC_INFO, "Could not create IODevice for client"); + + knownNodes.insert(connection); + qCDebug(QT_REMOTEOBJECT) << "Replica Connection isValid" << connection->isOpen(); + connect(connection, SIGNAL(shouldReconnect(ClientIoDevice*)), this, SLOT(onShouldReconnect(ClientIoDevice*))); + connection->connectToServer(); + connect(connection, SIGNAL(readyRead()), &clientRead, SLOT(map())); + clientRead.setMapping(connection, connection); +} + +bool QRemoteObjectNodePrivate::hasInstance(const QString &name) +{ + if (!replicas.contains(name)) + return false; + + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(name).toStrongRef(); + if (!rep) { //already deleted + replicas.remove(name); + return false; + } + + return true; +} + +void QRemoteObjectNodePrivate::onRemoteObjectSourceAdded(const QRemoteObjectSourceLocation &entry) +{ + qCDebug(QT_REMOTEOBJECT) <<"onRemoteObjectSourceAdded"<< entry << replicas<<replicas.contains(entry.first); + if (!entry.first.isEmpty()) { + QRemoteObjectSourceLocations locs = registry->propAsVariant(0).value<QRemoteObjectSourceLocations>(); + locs[entry.first] = entry.second; + //TODO Is there a way to extend QRemoteObjectSourceLocations in place? + registry->setProperty(0, QVariant::fromValue(locs)); + qCDebug(QT_REMOTEOBJECT) << "onRemoteObjectSourceAdded, now locations =" << registry->propAsVariant(0).value<QRemoteObjectSourceLocations>()<<locs; + } + if (replicas.contains(entry.first)) //We have a replica waiting on this remoteObject + { + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(entry.first).toStrongRef(); + if (!rep) { //replica has been deleted, remove from list + replicas.remove(entry.first); + return; + } + + initConnection(entry.second); + + qCDebug(QT_REMOTEOBJECT) << "Called initConnection due to new RemoteObjectSource added via registry" << entry.first; + } +} + +void QRemoteObjectNodePrivate::onRemoteObjectSourceRemoved(const QRemoteObjectSourceLocation &entry) +{ + if (!entry.first.isEmpty()) { + QRemoteObjectSourceLocations locs = registry->propAsVariant(0).value<QRemoteObjectSourceLocations>(); + locs.remove(entry.first); + registry->setProperty(0, QVariant::fromValue(locs)); + } +} + +void QRemoteObjectNodePrivate::onRegistryInitialized() +{ + qCDebug(QT_REMOTEOBJECT) << "Registry Initialized" << remoteObjectAddresses(); + + QHashIterator<QString, QUrl> i(remoteObjectAddresses()); + while (i.hasNext()) { + i.next(); + if (replicas.contains(i.key())) //We have a replica waiting on this remoteObject + { + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(i.key()).toStrongRef(); + if (rep && !requestedUrls.contains(i.value())) + initConnection(i.value()); + else if (!rep) //replica has been deleted, remove from list + replicas.remove(i.key()); + + continue; + } + } +} + +void QRemoteObjectNodePrivate::onShouldReconnect(ClientIoDevice *ioDevice) +{ + pendingReconnect.insert(ioDevice); + + Q_FOREACH (const QString &remoteObject, ioDevice->remoteObjects()) { + connectedSources.remove(remoteObject); + ioDevice->removeSource(remoteObject); + if (replicas.contains(remoteObject)) { //We have a replica waiting on this remoteObject + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(remoteObject).toStrongRef(); + QConnectedReplicaPrivate *cRep = static_cast<QConnectedReplicaPrivate*>(rep.data()); + if (rep && !cRep->connectionToSource.isNull()) { + cRep->setDisconnected(); + } else if (!rep) { + replicas.remove(remoteObject); + } + } + } + if (!reconnectTimer.isActive()) { + reconnectTimer.start(retryInterval, this); + qCDebug(QT_REMOTEOBJECT) << "Starting reconnect timer"; + } +} + +void QRemoteObjectNodePrivate::onClientRead(QObject *obj) +{ + ClientIoDevice *connection = qobject_cast<ClientIoDevice*>(obj); + Q_ASSERT(connection); + + if (!connection->read()) + return; + + using namespace QRemoteObjectPackets; + + const QRemoteObjectPacket* packet = connection->packet(); + switch (packet->id) { + case QRemoteObjectPacket::ObjectList: + { + const QObjectListPacket *p = static_cast<const QObjectListPacket *>(packet); + const QSet<QString> newObjects = p->objects.toSet(); + qCDebug(QT_REMOTEOBJECT) << "newObjects:" << newObjects; + Q_FOREACH (const QString &remoteObject, newObjects) { + qCDebug(QT_REMOTEOBJECT) << " connectedSources.contains("<<remoteObject<<")"<<connectedSources.contains(remoteObject)<<replicas.contains(remoteObject); + if (!connectedSources.contains(remoteObject)) { + connectedSources[remoteObject] = connection; + connection->addSource(remoteObject); + if (replicas.contains(remoteObject)) //We have a replica waiting on this remoteObject + { + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(remoteObject).toStrongRef(); + QConnectedReplicaPrivate *cRep = static_cast<QConnectedReplicaPrivate*>(rep.data()); + if (rep && cRep->connectionToSource.isNull()) + { + qCDebug(QT_REMOTEOBJECT) << "Test" << remoteObject<<replicas.keys(); + qCDebug(QT_REMOTEOBJECT) << cRep; + cRep->setConnection(connection); + } else if (!rep) { //replica has been deleted, remove from list + replicas.remove(remoteObject); + } + + continue; + } + } + } + break; + } + case QRemoteObjectPacket::InitPacket: + { + const QInitPacket *p = static_cast<const QInitPacket *>(packet); + const QString object = p->name; + qCDebug(QT_REMOTEOBJECT) << "InitObject-->" <<object << this; + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(object).toStrongRef(); + if (rep) + { + QConnectedReplicaPrivate *cRep = static_cast<QConnectedReplicaPrivate*>(rep.data()); + cRep->initialize(p->packetData); + } else { //replica has been deleted, remove from list + replicas.remove(object); + } + break; + } + case QRemoteObjectPacket::InitDynamicPacket: + { + const QInitDynamicPacket *p = static_cast<const QInitDynamicPacket *>(packet); + const QString object = p->name; + qCDebug(QT_REMOTEOBJECT) << "InitObject-->" <<object << this; + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(object).toStrongRef(); + if (rep) + { + QConnectedReplicaPrivate *cRep = static_cast<QConnectedReplicaPrivate*>(rep.data()); + cRep->initializeMetaObject(p); + + } else { //replica has been deleted, remove from list + replicas.remove(object); + } + break; + } + case QRemoteObjectPacket::RemoveObject: + { + const QRemoveObjectPacket *p = static_cast<const QRemoveObjectPacket *>(packet); + qCDebug(QT_REMOTEOBJECT) << "RemoveObject-->" << p->name << this; + connectedSources.remove(p->name); + connection->removeSource(p->name); + if (replicas.contains(p->name)) { //We have a replica waiting on this remoteObject + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(p->name).toStrongRef(); + QConnectedReplicaPrivate *cRep = static_cast<QConnectedReplicaPrivate*>(rep.data()); + if (rep && !cRep->connectionToSource.isNull()) { + cRep->connectionToSource.clear(); + if (cRep->isReplicaValid()) { + //Changed from receiving to not receiving + cRep->emitValidChanged(); + } + } else if (!rep) { + replicas.remove(p->name); + } + } + break; + } + case QRemoteObjectPacket::PropertyChangePacket: + { + const QPropertyChangePacket *p = static_cast<const QPropertyChangePacket *>(packet); + const QString object = p->name; + qCDebug(QT_REMOTEOBJECT) << "PropertyChange-->" << p->name << QString::fromLatin1(p->propertyName.constData()); + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(object).toStrongRef(); + if (rep) + { + const int offset = rep->m_metaObject->propertyOffset(); + const int index = rep->m_metaObject->indexOfProperty(p->propertyName.constData())-offset; + rep->setProperty(index, p->value); + } else { //replica has been deleted, remove from list + replicas.remove(p->name); + } + break; + } + case QRemoteObjectPacket::InvokePacket: + { + const QInvokePacket *p = static_cast<const QInvokePacket *>(packet); + QSharedPointer<QRemoteObjectReplicaPrivate> rep = replicas.value(p->name).toStrongRef(); + if (rep) + { + static QVariant null(QMetaType::QObjectStar, (void*)0); + + // Qt usually supports 9 arguments, so ten should be usually safe + QVarLengthArray<void*, 10> param(p->args.size() + 1); + param[0] = null.data(); //Never a return value + for (int i = 0; i < p->args.size(); i++) { + param[i + 1] = const_cast<void *>(p->args[i].data()); + } + qCDebug(QT_REMOTEOBJECT) << "Replica Invoke-->" << p->name << rep->m_metaObject->method(p->index+rep->m_methodOffset).name() << p->index << rep->m_methodOffset; + QMetaObject::activate(rep.data(), rep->metaObject(), p->index+rep->m_methodOffset, param.data()); + } else { //replica has been deleted, remove from list + replicas.remove(p->name); + } + break; + } + } + + if (connection->bytesAvailable()) //have bytes left over, so recurse + onClientRead(connection); +} + + +QRemoteObjectNode::QRemoteObjectNode() + : d_ptr(new QRemoteObjectNodePrivate) +{ + qRegisterMetaTypeStreamOperators<QVector<int> >(); +} + +QRemoteObjectNode::QRemoteObjectNode(const QUrl &hostAddress, const QUrl ®istryAddress) + : d_ptr(new QRemoteObjectNodePrivate) +{ + qRegisterMetaTypeStreamOperators<QVector<int> >(); + if (!hostAddress.isEmpty()) { + setHostUrl(hostAddress); + if (hostAddress == registryAddress) { + hostRegistry(); + return; + } + } + + if (!registryAddress.isEmpty()) + setRegistryUrl(registryAddress); +} + +QRemoteObjectNode::~QRemoteObjectNode() +{ +} + +QUrl QRemoteObjectNode::hostUrl() const +{ + if (d_ptr->remoteObjectIo.isNull()) + return QUrl(); + + return d_ptr->remoteObjectIo->serverAddress(); +} + +bool QRemoteObjectNode::setHostUrl(const QUrl &hostAddress) +{ + if (!d_ptr->remoteObjectIo.isNull()) { + d_ptr->m_lastError = ServerAlreadyCreated; + return false; + } + else if (d_ptr->isInitialized.loadAcquire()) { + d_ptr->m_lastError = RegistryAlreadyHosted; + return false; + } + + d_ptr->remoteObjectIo.reset(new QRemoteObjectSourceIo(hostAddress)); + //Since we don't know whether setHostUrl or setRegistryUrl/setRegistryHost will be called first, + //break it into two pieces. setHostUrl connects the RemoteObjectSourceIo->[add/remove]RemoteObjectSource to QRemoteObjectReplicaNode->[add/remove]RemoteObjectSource + //setRegistry* calls appropriately connect RemoteObjecSourcetIo->[add/remove]RemoteObjectSource to the registry when it is created + QObject::connect(d_ptr->remoteObjectIo.data(), SIGNAL(remoteObjectAdded(QRemoteObjectSourceLocation)), d_ptr.data(), SIGNAL(remoteObjectAdded(QRemoteObjectSourceLocation))); + QObject::connect(d_ptr->remoteObjectIo.data(), SIGNAL(remoteObjectRemoved(QRemoteObjectSourceLocation)), d_ptr.data(), SIGNAL(remoteObjectRemoved(QRemoteObjectSourceLocation))); + + return true; +} + +QRemoteObjectNode::ErrorCode QRemoteObjectNode::lastError() const +{ + return d_ptr->m_lastError; +} + +QUrl QRemoteObjectNode::registryUrl() const +{ + return d_ptr->registryAddress; +} + +bool QRemoteObjectNode::setRegistryUrl(const QUrl ®istryAddress) +{ + if (d_ptr->isInitialized.loadAcquire() || ! d_ptr->registry.isNull()) { + d_ptr->m_lastError = RegistryAlreadyHosted; + return false; + } + + connect(registryAddress); + d_ptr->registryAddress = registryAddress; + d_ptr->setRegistry(acquire<QRemoteObjectRegistry>()); + //Connect remoteObject[Added/Removed] to the registry Slot + QObject::connect(d_ptr.data(), SIGNAL(remoteObjectAdded(QRemoteObjectSourceLocation)), d_ptr->registry.data(), SLOT(addSource(QRemoteObjectSourceLocation))); + QObject::connect(d_ptr.data(), SIGNAL(remoteObjectRemoved(QRemoteObjectSourceLocation)), d_ptr->registry.data(), SLOT(removeSource(QRemoteObjectSourceLocation))); + //TODO - what should happen if you register a RemoteObjectSource on the Registry node, but the RegistrySource isn't connected? + //Possible to have a way to cache the values, so they can be sent when a connection is made + //Or, return false on enableRemoting, with an error about not having the registry? + //Or possible to get the list of RemoteObjectSources from remoteObjectIo, and send when the connection is made (use that as the cache) + return d_ptr->registry->waitForSource(); +} + +void QRemoteObjectNodePrivate::setRegistry(QRemoteObjectRegistry *reg) +{ + registry.reset(reg); + //Make sure when we get the registry initialized, we update our replicas + QObject::connect(reg, SIGNAL(initialized()), this, SLOT(onRegistryInitialized())); + //Make sure we handle new RemoteObjectSources on Registry... + QObject::connect(reg, SIGNAL(remoteObjectAdded(QRemoteObjectSourceLocation)), this, SLOT(onRemoteObjectSourceAdded(QRemoteObjectSourceLocation))); + QObject::connect(reg, SIGNAL(remoteObjectRemoved(QRemoteObjectSourceLocation)), this, SLOT(onRemoteObjectSourceRemoved(QRemoteObjectSourceLocation))); +} + +bool QRemoteObjectNode::hostRegistry() +{ + if (d_ptr->remoteObjectIo.isNull()) { + d_ptr->m_lastError = ServerAlreadyCreated; + return false; + } + else if (d_ptr->isInitialized.loadAcquire() || !d_ptr->registry.isNull()) { + d_ptr->m_lastError = RegistryAlreadyHosted; + return false; + } + + QRegistrySource *remoteObject = new QRegistrySource; + enableRemoting(remoteObject); + d_ptr->registryAddress = d_ptr->remoteObjectIo->serverAddress(); + d_ptr->registrySource.reset(remoteObject); + //Connect RemoteObjectSourceIo->remoteObject[Added/Removde] to the registry Slot + QObject::connect(d_ptr.data(), SIGNAL(remoteObjectAdded(QRemoteObjectSourceLocation)), d_ptr->registrySource.data(), SLOT(addSource(QRemoteObjectSourceLocation))); + QObject::connect(d_ptr.data(), SIGNAL(remoteObjectRemoved(QRemoteObjectSourceLocation)), d_ptr->registrySource.data(), SLOT(removeSource(QRemoteObjectSourceLocation))); + QObject::connect(d_ptr->remoteObjectIo.data(), SIGNAL(serverRemoved(QUrl)),d_ptr->registrySource.data(), SLOT(removeServer(QUrl))); + //onAdd/Remove update the known remoteObjects list in the RegistrySource, so no need to connect to the RegistrySource remoteObjectAdded/Removed signals + d_ptr->setRegistry(acquire<QRemoteObjectRegistry>()); + return true; +} + +QRemoteObjectNode QRemoteObjectNode::createHostNode(const QUrl &hostAddress) +{ + return QRemoteObjectNode(hostAddress, QUrl()); +} + +QRemoteObjectNode QRemoteObjectNode::createRegistryHostNode(const QUrl &hostAddress) +{ + return QRemoteObjectNode(hostAddress, hostAddress); +} + +QRemoteObjectNode QRemoteObjectNode::createNodeConnectedToRegistry(const QUrl ®istryAddress) +{ + return QRemoteObjectNode(QUrl(), registryAddress); +} + +QRemoteObjectNode QRemoteObjectNode::createHostNodeConnectedToRegistry(const QUrl &hostAddress, const QUrl ®istryAddress) +{ + if (hostAddress == registryAddress) { //Assume hosting registry is NOT intended + QRemoteObjectNode node(hostAddress, QUrl()); + node.d_ptr->m_lastError = UnintendedRegistryHosting; + return node; + } + + return QRemoteObjectNode(hostAddress, registryAddress); +} + +void QRemoteObjectNode::connect(const QUrl &address) +{ + d_ptr->initConnection(address); +} + +QRemoteObjectDynamicReplica *QRemoteObjectNode::acquire(const QString &name) +{ + QRemoteObjectDynamicReplica *instance = new QRemoteObjectDynamicReplica; + return static_cast<QRemoteObjectDynamicReplica*>(d_ptr->acquire(Q_NULLPTR, instance, name)); +} + +bool QRemoteObjectNode::enableRemoting(QRemoteObjectSource *remoteObject) +{ + if (d_ptr->remoteObjectIo.isNull()) { + d_ptr->m_lastError = OperationNotValidOnClientNode; + return false; + } + + const int ind = remoteObject->metaObject()->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE); + const QString name = QString::fromLatin1(remoteObject->metaObject()->classInfo(ind).value()); + + d_ptr->isInitialized.storeRelease(1); + + return d_ptr->remoteObjectIo->enableRemoting(remoteObject, &QRemoteObjectSource::staticMetaObject, name); +} + +bool QRemoteObjectNode::enableRemoting(QObject *object, const QMetaObject *_meta) +{ + if (d_ptr->remoteObjectIo.isNull()) { + d_ptr->m_lastError = OperationNotValidOnClientNode; + return false; + } + + const QMetaObject *meta = _meta; + QString name; + if (!meta) { //If meta isn't provided, we need to search for an object that has RemoteObject CLASSINFO + meta = object->metaObject(); + int ind = meta->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE); + while (meta && ind == -1) { + meta = meta->superClass(); + if (meta) + ind = meta->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE); + } + if (meta) { + name = QString::fromLatin1(meta->classInfo(ind).value()); + meta = meta->superClass(); //We want the parent of the class that has ClassInfo, since we want to forward + //the object_type API + } else { + name = object->objectName(); + if (name.isEmpty()) { + d_ptr->m_lastError = MissingObjectName; + qCWarning(QT_REMOTEOBJECT) << "enableRemoting() Error: Unable to Replicate an object that does not have objectName() set."; + return false; + } + meta = object->metaObject()->superClass(); //*Assume* we only want object's API forwarded + } + } else { + name = object->objectName(); + if (name.isEmpty()) { + d_ptr->m_lastError = MissingObjectName; + qCWarning(QT_REMOTEOBJECT) << "enableRemoting() Error: Unable to Replicate an object that does not have objectName() set."; + return false; + } + const QMetaObject *check = object->metaObject(); + if (check == meta) { + qCWarning(QT_REMOTEOBJECT) << "enableRemoting() Error: The QMetaObject pointer provided is for object. An ancestor of object should be used."; + return false; + } + while (check && check != meta) + check = check->superClass(); + if (!check) { //Oops, meta is not a superclass of object + qCWarning(QT_REMOTEOBJECT) << "enableRemoting() Error: The QMetaObject must be an ancestor of object."; + return false; + } + } + + return d_ptr->remoteObjectIo->enableRemoting(object, meta, name); +} + +bool QRemoteObjectNode::disableRemoting(QObject *remoteObject) +{ + if (d_ptr->remoteObjectIo.isNull()) { + d_ptr->m_lastError = OperationNotValidOnClientNode; + return false; + } + + QRemoteObjectSourcePrivate *pp = remoteObject->findChild<QRemoteObjectSourcePrivate *>(QString(), Qt::FindDirectChildrenOnly); + if (!pp) //We must be calling unregister before register was called. + return false; + + if (!d_ptr->remoteObjectIo->disableRemoting(pp)) { + d_ptr->m_lastError = SourceNotRegistered; + return false; + } + + return true; +} + +QRemoteObjectReplica *QRemoteObjectNode::acquire(const QMetaObject *replicaMeta, QRemoteObjectReplica *instance) +{ + return d_ptr->acquire(replicaMeta, instance, name(replicaMeta)); +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectnode.h b/src/remoteobjects/qremoteobjectnode.h new file mode 100644 index 0000000..2a5dced --- /dev/null +++ b/src/remoteobjects/qremoteobjectnode.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTNODE_H +#define QREMOTEOBJECTNODE_H + +#include "qtremoteobjectglobal.h" + +#include "qremoteobjectregistry.h" +#include "qremoteobjectdynamicreplica.h" + +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE + +class QRemoteObjectSource; +class QRemoteObjectReplica; +class QRemoteObjectNodePrivate; + +class Q_REMOTEOBJECTS_EXPORT QRemoteObjectNode +{ +public: + + enum ErrorCode{ + NoError, + RegistryNotAcquired, + RegistryAlreadyHosted, + NodeIsNoServer, + ServerAlreadyCreated, + UnintendedRegistryHosting, + OperationNotValidOnClientNode, + SourceNotRegistered, + MissingObjectName + }; + + QRemoteObjectNode(); + ~QRemoteObjectNode(); + + //TODO maybe set defaults from a #define to allow override? + static QRemoteObjectNode createHostNode(const QUrl &hostAddress = QUrl(QString::fromLatin1("local:replica"))); + static QRemoteObjectNode createRegistryHostNode(const QUrl &hostAddress = QUrl(QString::fromLatin1("local:registry"))); + static QRemoteObjectNode createNodeConnectedToRegistry(const QUrl ®istryAddress = QUrl(QString::fromLatin1("local:registry"))); + static QRemoteObjectNode createHostNodeConnectedToRegistry(const QUrl &hostAddress = QUrl(QString::fromLatin1("local:replica")), + const QUrl ®istryAddress = QUrl(QString::fromLatin1("local:registry"))); + QUrl hostUrl() const; + bool setHostUrl(const QUrl &hostAddress); + QUrl registryUrl() const; + bool setRegistryUrl(const QUrl ®istryAddress); + bool hostRegistry(); + void connect(const QUrl &address=QUrl(QString::fromLatin1("local:replica"))); + const QRemoteObjectRegistry *registry() const; + template < class ObjectType > + ObjectType *acquire() + { + ObjectType* replica = new ObjectType; + return qobject_cast< ObjectType* >(acquire(&ObjectType::staticMetaObject, replica)); + } + QRemoteObjectDynamicReplica *acquire(const QString &name); + + bool enableRemoting(QRemoteObjectSource *remoteObject); + bool enableRemoting(QObject *object, const QMetaObject *meta = Q_NULLPTR); + bool disableRemoting(QObject *remoteObject); + + ErrorCode lastError() const; + +private: + QRemoteObjectNode(const QUrl &hostAddress, const QUrl ®istryAddress); + QRemoteObjectReplica *acquire(const QMetaObject *, QRemoteObjectReplica *); + + QSharedPointer<QRemoteObjectNodePrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectnode_p.h b/src/remoteobjects/qremoteobjectnode_p.h new file mode 100644 index 0000000..aa28cef --- /dev/null +++ b/src/remoteobjects/qremoteobjectnode_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTNODE_P_H +#define QREMOTEOBJECTNODE_P_H + +#include "qconnectionclientfactory_p.h" +#include "qremoteobjectsourceio_p.h" +#include "qremoteobjectreplica.h" +#include "qremoteobjectnode.h" + +#include <QBasicTimer> +#include <QMutex> +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE + +class QRemoteObjectRegistry; +class QRegistrySource; + +class QRemoteObjectNodePrivate : public QObject +{ + Q_OBJECT + +public: + QRemoteObjectNodePrivate(); + virtual ~QRemoteObjectNodePrivate(); + + QRemoteObjectSourceLocations remoteObjectAddresses() const; + + void timerEvent(QTimerEvent*); + + QRemoteObjectReplica *acquire(const QMetaObject *, QRemoteObjectReplica *, const QString &); + + void connectReplica(QObject *object, QRemoteObjectReplica *instance); + void openConnectionIfNeeded(const QString &name); + + void initConnection(const QUrl &address); + bool hasInstance(const QString &name); + void setRegistry(QRemoteObjectRegistry *); + +Q_SIGNALS: + void remoteObjectAdded(const QRemoteObjectSourceLocation &); + void remoteObjectRemoved(const QRemoteObjectSourceLocation &); + +public Q_SLOTS: + void onClientRead(QObject *obj); + void onRemoteObjectSourceAdded(const QRemoteObjectSourceLocation &entry); + void onRemoteObjectSourceRemoved(const QRemoteObjectSourceLocation &entry); + void onRegistryInitialized(); + void onShouldReconnect(ClientIoDevice *ioDevice); + +public: + QAtomicInt isInitialized; + QScopedPointer<QRemoteObjectSourceIo> remoteObjectIo; + QMutex mutex; + QUrl registryAddress; + QHash<QString, QWeakPointer<QRemoteObjectReplicaPrivate> > replicas; + QConnectionClientFactory m_factory; + QMap<QString, ClientIoDevice*> connectedSources; + QSet<ClientIoDevice*> knownNodes; + QSet<ClientIoDevice*> pendingReconnect; + QSet<QUrl> requestedUrls; + QSignalMapper clientRead; + QScopedPointer<QRemoteObjectRegistry> registry; + QScopedPointer<QRegistrySource> registrySource; + int retryInterval; + QBasicTimer reconnectTimer; + QRemoteObjectNode::ErrorCode m_lastError; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectregistry.cpp b/src/remoteobjects/qremoteobjectregistry.cpp new file mode 100644 index 0000000..f292070 --- /dev/null +++ b/src/remoteobjects/qremoteobjectregistry.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteobjectregistry.h" +#include "qremoteobjectreplica_p.h" + +#include <QDataStream> + +QT_BEGIN_NAMESPACE + +QRemoteObjectRegistry::QRemoteObjectRegistry(QObject *parent) : QRemoteObjectReplica(parent) +{ +} + +QRemoteObjectRegistry::~QRemoteObjectRegistry() +{} + +void QRemoteObjectRegistry::initialize() +{ + qRegisterMetaType<QRemoteObjectSourceLocation>(); + qRegisterMetaTypeStreamOperators<QRemoteObjectSourceLocation>(); + qRegisterMetaType<QRemoteObjectSourceLocations>(); + qRegisterMetaTypeStreamOperators<QRemoteObjectSourceLocations>(); + QVariantList properties; + properties.reserve(3); + properties << QVariant::fromValue(QRemoteObjectSourceLocations()); + properties << QVariant::fromValue(QRemoteObjectSourceLocation()); + properties << QVariant::fromValue(QRemoteObjectSourceLocation()); + setProperties(properties); +} + +QRemoteObjectSourceLocations QRemoteObjectRegistry::sourceLocations() const +{ + return propAsVariant(0).value<QRemoteObjectSourceLocations>(); +} + +void QRemoteObjectRegistry::addSource(const QRemoteObjectSourceLocation &entry) +{ + if (!isInitialized()) { + bool res = waitForSource(); + if (!res) + return; //FIX What to do here? + } + qCDebug(QT_REMOTEOBJECT) << "An entry was added to the registry - Sending to Source" << entry.first << entry.second; + // This does not set any data to avoid a coherency problem between client and server + static int index = QRemoteObjectRegistry::staticMetaObject.indexOfMethod("addSource(QRemoteObjectSourceLocation)"); + QVariantList args; + args << QVariant::fromValue(entry); + send(QMetaObject::InvokeMetaMethod, index, args); +} + +void QRemoteObjectRegistry::removeSource(const QRemoteObjectSourceLocation &entry) +{ + qCDebug(QT_REMOTEOBJECT) << "An entry was removed from the registry - Sending to Source" << entry.first << entry.second; + // This does not set any data to avoid a coherency problem between client and server + static int index = QRemoteObjectRegistry::staticMetaObject.indexOfMethod("removeSource(QRemoteObjectSourceLocation)"); + QVariantList args; + args << QVariant::fromValue(entry); + send(QMetaObject::InvokeMetaMethod, index, args); +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectregistry.h b/src/remoteobjects/qremoteobjectregistry.h new file mode 100644 index 0000000..4e21cb8 --- /dev/null +++ b/src/remoteobjects/qremoteobjectregistry.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTREGISTRY_P_H +#define QREMOTEOBJECTREGISTRY_P_H + +#include "qremoteobjectreplica.h" + +QT_BEGIN_NAMESPACE + +class Q_REMOTEOBJECTS_EXPORT QRemoteObjectRegistry : public QRemoteObjectReplica +{ + Q_OBJECT + Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, "Registry") + + Q_PROPERTY(QRemoteObjectSourceLocations sourceLocations READ sourceLocations) + + friend class QRemoteObjectNode; + +public: + ~QRemoteObjectRegistry(); + void initialize() Q_DECL_OVERRIDE; + + QRemoteObjectSourceLocations sourceLocations() const; + +Q_SIGNALS: + void remoteObjectAdded(const QRemoteObjectSourceLocation &entry); + void remoteObjectRemoved(const QRemoteObjectSourceLocation &entry); + +public Q_SLOTS: + void addSource(const QRemoteObjectSourceLocation &entry); + void removeSource(const QRemoteObjectSourceLocation &entry); + +private: + explicit QRemoteObjectRegistry(QObject *parent = Q_NULLPTR); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectreplica.cpp b/src/remoteobjects/qremoteobjectreplica.cpp new file mode 100644 index 0000000..1f34a16 --- /dev/null +++ b/src/remoteobjects/qremoteobjectreplica.cpp @@ -0,0 +1,404 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteobjectreplica.h" +#include "qremoteobjectreplica_p.h" +#include "qremoteobjectdynamicreplica.h" +#include "qconnectionclientfactory_p.h" +#include "qremoteobjectsource_p.h" +#include "private/qmetaobjectbuilder_p.h" + +#include <QCoreApplication> +#include <QDataStream> +#include <QVariant> +#include <QMetaProperty> +#include <QThread> +#include <QTime> + +QT_BEGIN_NAMESPACE + +using namespace QRemoteObjectPackets; + +QRemoteObjectReplicaPrivate::QRemoteObjectReplicaPrivate(const QString &name, const QMetaObject *meta) + : QObject(Q_NULLPTR), m_objectName(name), m_metaObject(meta), + m_methodOffset(meta ? QRemoteObjectReplica::staticMetaObject.methodCount() : QRemoteObjectDynamicReplica::staticMetaObject.methodCount()), + m_propertyOffset(meta ? QRemoteObjectReplica::staticMetaObject.propertyCount() : QRemoteObjectDynamicReplica::staticMetaObject.propertyCount()) +{ +} + +QRemoteObjectReplicaPrivate::~QRemoteObjectReplicaPrivate() +{ + if (m_metaObject && qstrcmp(m_metaObject->className(), "QRemoteObjectDynamicReplica") == 0) + delete m_metaObject; +} + +QConnectedReplicaPrivate::QConnectedReplicaPrivate(const QString &name, const QMetaObject *meta) + : QRemoteObjectReplicaPrivate(name, meta), isSet(0), connectionToSource(Q_NULLPTR) +{ +} + +QConnectedReplicaPrivate::~QConnectedReplicaPrivate() +{ + if (!connectionToSource.isNull()) { + qCDebug(QT_REMOTEOBJECT) << "Replica deleted: sending RemoveObject to RemoteObjectSource" << m_objectName; + QRemoveObjectPacket packet(m_objectName); + sendCommand(&packet); + } +} + +bool QRemoteObjectReplicaPrivate::isDynamicReplica() const +{ + return m_metaObject == Q_NULLPTR; +} + +void QConnectedReplicaPrivate::sendCommand(const QRemoteObjectPacket *packet) +{ + Q_ASSERT(!connectionToSource.isNull()); + + if (!connectionToSource->isOpen()) + return; + + connectionToSource->write(packet->serialize()); +} + +void QConnectedReplicaPrivate::initialize(const QByteArray &packetData) +{ + qCDebug(QT_REMOTEOBJECT) << "initialize()" << m_propertyStorage.size(); + quint32 nParam, len; + QDataStream in(packetData); + in >> nParam; + QVector<int> signalList; + QVariant value; + const int offset = m_metaObject->propertyOffset(); + for (quint32 i = 0; i < nParam; ++i) { + in >> len; + qint64 pos = in.device()->pos(); + const int index = m_metaObject->indexOfProperty(packetData.constData()+pos) - offset; + in.skipRawData(len); //Skip property name char *, since we used it in-place ^^ + in >> value; + qCDebug(QT_REMOTEOBJECT) << " in loop" << index << m_propertyStorage.size(); + if (index >= 0 && m_propertyStorage[index] != value) { + m_propertyStorage[index] = value; + int notifyIndex = m_metaObject->property(index+offset).notifySignalIndex(); + if (notifyIndex >= 0) + signalList.append(notifyIndex); + } + qCDebug(QT_REMOTEOBJECT) << "SETPROPERTY" << index << m_metaObject->property(index+offset).name() << value.typeName() << value.toString(); + } + + //Note: Because we are generating the notify signals ourselves, we know there will be no parameters + //This allows us to pass noArgs to qt_metacall + void *noArgs[] = {0}; + Q_FOREACH (int index, signalList) { + qCDebug(QT_REMOTEOBJECT) << " Before activate" << index << m_metaObject->property(index).name(); + QMetaObject::activate(this, metaObject(), index, noArgs); + } + + //initialized and validChanged need to be sent manually, since they are not in the derived classes + if (isSet.fetchAndStoreRelease(2) > 0) { + //We are already initialized, now we are valid again + emitValidChanged(); + } else { + //We need to send the initialized signal, too + emitInitialized(); + emitValidChanged(); + } + qCDebug(QT_REMOTEOBJECT) << "isSet = true"; +} + +void QRemoteObjectReplicaPrivate::emitValidChanged() +{ + const static int validChangedIndex = QRemoteObjectReplica::staticMetaObject.indexOfMethod("onIsReplicaValidChanged()"); + Q_ASSERT(validChangedIndex != -1); + void *noArgs[] = {0}; + QMetaObject::activate(this, metaObject(), validChangedIndex, noArgs); +} + +void QRemoteObjectReplicaPrivate::emitInitialized() +{ + const static int initializedIndex = QRemoteObjectReplica::staticMetaObject.indexOfMethod("initialized()"); + Q_ASSERT(initializedIndex != -1); + void *noArgs[] = {0}; + QMetaObject::activate(this, metaObject(), initializedIndex, noArgs); +} + +void QRemoteObjectReplicaPrivate::initializeMetaObject(const QInitDynamicPacket *packet) +{ + Q_ASSERT(!m_metaObject); + + QMetaObjectBuilder builder; + builder.setClassName("QRemoteObjectDynamicReplica"); + builder.setSuperClass(&QRemoteObjectReplica::staticMetaObject); + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + + QVector<QPair<QByteArray, QVariant> > propertyValues; + m_metaObject = packet->createMetaObject(builder, m_remoteObjectMethodTypes, m_methodArgumentTypes, &propertyValues); + //rely on order of properties; + QVariantList list; + typedef QPair<QByteArray, QVariant> PropertyPair; + foreach (const PropertyPair &pair, propertyValues) + list << pair.second; + setProperties(list); +} + +void QConnectedReplicaPrivate::initializeMetaObject(const QInitDynamicPacket *packet) +{ + QRemoteObjectReplicaPrivate::initializeMetaObject(packet); + foreach (QRemoteObjectReplica *obj, m_parentsNeedingConnect) + configurePrivate(obj); + m_parentsNeedingConnect.clear(); + void *noArgs[] = {0}; + for (int index = 0; index < metaObject()->propertyCount(); ++index) { + qCDebug(QT_REMOTEOBJECT) << " Before activate" << index << m_metaObject->property(index).name(); + QMetaObject::activate(this, metaObject(), index, noArgs); + } + //initialized and validChanged need to be sent manually, since they are not in the derived classes + if (isSet.fetchAndStoreRelease(2) > 0) { + //We are already initialized, now we are valid again + emitValidChanged(); + } else { + //We need to send the initialized signal, too + emitInitialized(); + emitValidChanged(); + } + qCDebug(QT_REMOTEOBJECT) << "isSet = true"; +} + +bool QConnectedReplicaPrivate::isInitialized() const +{ + return isSet.load() > 0; +} + +bool QConnectedReplicaPrivate::isReplicaValid() const +{ + qCDebug(QT_REMOTEOBJECT) << "isReplicaValid()" << isSet.load(); + + return isSet.load() == 2; +} + +bool QConnectedReplicaPrivate::waitForSource(int timeout) +{ + if (isSet.load() != 2) { + QTime t; + t.start(); + + while (isSet.load() != 2) { + if (t.elapsed() > timeout) { + qCWarning(QT_REMOTEOBJECT) << "Timeout waiting for client to get set" << m_objectName; + return false; + } + + qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 10); + } + } + + return true; +} + +void QConnectedReplicaPrivate::_q_send(QMetaObject::Call call, int index, const QVariantList &args) +{ + Q_ASSERT(call == QMetaObject::InvokeMetaMethod || call == QMetaObject::WriteProperty); + + if (call == QMetaObject::InvokeMetaMethod) { + qCDebug(QT_REMOTEOBJECT) << "Send" << call << this->m_metaObject->method(index).name() << index << args << connectionToSource; + QInvokePacket package = QInvokePacket(m_objectName, call, index - m_methodOffset, args); + sendCommand(&package); + } else { + qCDebug(QT_REMOTEOBJECT) << "Send" << call << this->m_metaObject->property(index).name() << index << args << connectionToSource; + QInvokePacket package = QInvokePacket(m_objectName, call, index - m_propertyOffset, args); + sendCommand(&package); + } +} + +const QVariant QConnectedReplicaPrivate::getProperty(int i) const +{ + return m_propertyStorage[i]; +} + +void QConnectedReplicaPrivate::setProperties(const QVariantList &properties) +{ + Q_ASSERT(m_propertyStorage.isEmpty()); + m_propertyStorage.reserve(properties.length()); + m_propertyStorage = properties; +} + +void QConnectedReplicaPrivate::setProperty(int i, const QVariant &prop) +{ + m_propertyStorage[i] = prop; +} + +void QConnectedReplicaPrivate::setConnection(ClientIoDevice *conn) +{ + if (connectionToSource.isNull()) { + connectionToSource = conn; + qCDebug(QT_REMOTEOBJECT) << "setConnection started" << conn << m_objectName; + } + requestRemoteObjectSource(); +} + +void QConnectedReplicaPrivate::setDisconnected() +{ + connectionToSource.clear(); + if (isSet.fetchAndStoreRelease(1) == 2) + emitValidChanged(); +} + +void QConnectedReplicaPrivate::requestRemoteObjectSource() +{ + QAddObjectPacket packet(m_objectName, isDynamicReplica()); + sendCommand(&packet); +} + +void QRemoteObjectReplicaPrivate::configurePrivate(QRemoteObjectReplica *rep) +{ + for (int i = QRemoteObjectReplica::staticMetaObject.methodOffset(); i < m_metaObject->methodCount(); i++) { + QMetaMethod mm = m_metaObject->method(i); + if (mm.methodType() == QMetaMethod::Signal) { + const bool res = QMetaObject::connect(this, i, rep, i, Qt::DirectConnection, 0); + qCDebug(QT_REMOTEOBJECT) << " Connect"<<i<<res<<mm.name(); + Q_UNUSED(res); + } + } +} + +void QConnectedReplicaPrivate::configurePrivate(QRemoteObjectReplica *rep) +{ + if (m_metaObject) + QRemoteObjectReplicaPrivate::configurePrivate(rep); + else + m_parentsNeedingConnect.append(rep); +} + +QRemoteObjectReplica::QRemoteObjectReplica(QObject *parent) : QObject(parent) +{ +} + +QRemoteObjectReplica::~QRemoteObjectReplica() +{ +} + +void QRemoteObjectReplica::send(QMetaObject::Call call, int index, const QVariantList &args) +{ + Q_D(QRemoteObjectReplica); + + d->_q_send(call, index, args); +} + +const QVariant QRemoteObjectReplica::propAsVariant(int i) const +{ + Q_D(const QRemoteObjectReplica); + return d->getProperty(i); +} + +void QRemoteObjectReplica::setProperties(const QVariantList &properties) +{ + Q_D(QRemoteObjectReplica); + d->setProperties(properties); +} + +void QRemoteObjectReplica::setProperty(int i, const QVariant &prop) +{ + Q_D(QRemoteObjectReplica); + d->setProperty(i, prop); +} + +bool QRemoteObjectReplica::isInitialized() const +{ + Q_D(const QRemoteObjectReplica); + + return d->isInitialized(); +} + +void QRemoteObjectReplica::initialize() +{ +} + +bool QRemoteObjectReplica::isReplicaValid() const +{ + Q_D(const QRemoteObjectReplica); + + return d->isReplicaValid(); +} + +bool QRemoteObjectReplica::waitForSource(int timeout) +{ + Q_D(QRemoteObjectReplica); + + return d->waitForSource(timeout); +} + +QInProcessReplicaPrivate::QInProcessReplicaPrivate(const QString &name, const QMetaObject *meta) : QRemoteObjectReplicaPrivate(name, meta) +{ +} + +QInProcessReplicaPrivate::~QInProcessReplicaPrivate() +{ +} + +const QVariant QInProcessReplicaPrivate::getProperty(int i) const +{ + Q_ASSERT(connectionToSource); + Q_ASSERT(connectionToSource->m_object); + const int index = i + connectionToSource->m_propertyOffset; + Q_ASSERT(index >= 0 && index < connectionToSource->m_object->metaObject()->propertyCount()); + return connectionToSource->m_object->metaObject()->property(index).read(connectionToSource->m_object); +} + +void QInProcessReplicaPrivate::setProperties(const QVariantList &) +{ + //TODO some verification here maybe? +} + +void QInProcessReplicaPrivate::setProperty(int i, const QVariant &property) +{ + Q_ASSERT(connectionToSource); + Q_ASSERT(connectionToSource->m_object); + const int index = i+connectionToSource->m_propertyOffset; + Q_ASSERT(index >= 0 && index < connectionToSource->m_object->metaObject()->propertyCount()); + connectionToSource->m_object->metaObject()->property(index).write(connectionToSource->m_object, property); +} + +void QInProcessReplicaPrivate::_q_send(QMetaObject::Call call, int index, const QVariantList &args) +{ + connectionToSource->invoke(call, index, args); +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectreplica.h b/src/remoteobjects/qremoteobjectreplica.h new file mode 100644 index 0000000..0d0e92c --- /dev/null +++ b/src/remoteobjects/qremoteobjectreplica.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQREMOTEOBJECTREPLICA_H +#define QQREMOTEOBJECTREPLICA_H + +#include "qtremoteobjectglobal.h" + +#include <QSharedPointer> +#include <QtCore/qnamespace.h> + +QT_BEGIN_NAMESPACE + +class QRemoteObjectReplicaPrivate; + +class Q_REMOTEOBJECTS_EXPORT QRemoteObjectReplica : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool isReplicaValid READ isReplicaValid NOTIFY onIsReplicaValidChanged) + +public: + virtual ~QRemoteObjectReplica(); + + bool isReplicaValid() const; + bool waitForSource(int timeout = 100); + bool isInitialized() const; + virtual void initialize(); + +Q_SIGNALS: + void onIsReplicaValidChanged(); + void initialized(); + +protected: + explicit QRemoteObjectReplica(QObject *parent = Q_NULLPTR); + + void send(QMetaObject::Call call, int index, const QVariantList &args); +protected: + void setProperty(int i, const QVariant &); + void setProperties(const QVariantList &); + const QVariant propAsVariant(int i) const; + Q_DECLARE_PRIVATE(QRemoteObjectReplica) + QSharedPointer<QRemoteObjectReplicaPrivate> d_ptr; +private: + friend class QRemoteObjectNodePrivate; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectreplica_p.h b/src/remoteobjects/qremoteobjectreplica_p.h new file mode 100644 index 0000000..f2451e3 --- /dev/null +++ b/src/remoteobjects/qremoteobjectreplica_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTREPLICA_P_H +#define QREMOTEOBJECTREPLICA_P_H + +#include "qremoteobjectreplica.h" +#include <QPointer> +#include <QVector> +#include <qcompilerdetection.h> +#include "qtremoteobjectglobal.h" + +QT_BEGIN_NAMESPACE + +class QRemoteObjectReplica; +class QRemoteObjectSourcePrivate; +class ClientIoDevice; + +using namespace QRemoteObjectPackets; + +class QRemoteObjectReplicaPrivate : public QObject +{ +public: + explicit QRemoteObjectReplicaPrivate(const QString &name, const QMetaObject *); + virtual ~QRemoteObjectReplicaPrivate(); + + bool isDynamicReplica() const; + + virtual const QVariant getProperty(int i) const = 0; + virtual void setProperties(const QVariantList &) = 0; + virtual void setProperty(int i, const QVariant &) = 0; + virtual bool isShortCircuit() const = 0; + virtual bool isInitialized() const { return true; } + virtual bool isReplicaValid() const { return true; } + virtual bool waitForSource(int) { return true; } + virtual void configurePrivate(QRemoteObjectReplica *); + void emitValidChanged(); + void emitInitialized(); + + virtual void _q_send(QMetaObject::Call call, int index, const QVariantList &args) = 0; + + //Dynamic replica functions + virtual void initializeMetaObject(const QInitDynamicPacket *packet); + + QString m_objectName; + const QMetaObject *m_metaObject; + + //Dynamic Replica data + QVector<QVector<int> > m_methodArgumentTypes; + QVector<int> m_remoteObjectMethodTypes; + int m_methodOffset, m_propertyOffset; +}; + +class QConnectedReplicaPrivate : public QRemoteObjectReplicaPrivate +{ +public: + explicit QConnectedReplicaPrivate(const QString &name, const QMetaObject *); + virtual ~QConnectedReplicaPrivate(); + const QVariant getProperty(int i) const Q_DECL_OVERRIDE; + void setProperties(const QVariantList &) Q_DECL_OVERRIDE; + void setProperty(int i, const QVariant &) Q_DECL_OVERRIDE; + bool isShortCircuit() const Q_DECL_OVERRIDE { return false; } + bool isInitialized() const Q_DECL_OVERRIDE; + bool isReplicaValid() const Q_DECL_OVERRIDE; + bool waitForSource(int timeout) Q_DECL_OVERRIDE; + void initialize(const QByteArray &); + void configurePrivate(QRemoteObjectReplica *) Q_DECL_OVERRIDE; + void requestRemoteObjectSource(); + void sendCommand(const QRemoteObjectPackets::QRemoteObjectPacket *packet); + void setConnection(ClientIoDevice *conn); + void setDisconnected(); + void _q_send(QMetaObject::Call call, int index, const QVariantList &args) Q_DECL_OVERRIDE; + void initializeMetaObject(const QInitDynamicPacket *packet) Q_DECL_OVERRIDE; + QAtomicInt isSet; + QVector<QRemoteObjectReplica *> m_parentsNeedingConnect; + QVariantList m_propertyStorage; + QPointer<ClientIoDevice> connectionToSource; +}; + +class QInProcessReplicaPrivate : public QRemoteObjectReplicaPrivate +{ +public: + explicit QInProcessReplicaPrivate(const QString &name, const QMetaObject *); + virtual ~QInProcessReplicaPrivate(); + + const QVariant getProperty(int i) const Q_DECL_OVERRIDE; + void setProperties(const QVariantList &) Q_DECL_OVERRIDE; + void setProperty(int i, const QVariant &) Q_DECL_OVERRIDE; + bool isShortCircuit() const Q_DECL_OVERRIDE { return true; } + + virtual void _q_send(QMetaObject::Call call, int index, const QVariantList &args); + QPointer<QRemoteObjectSourcePrivate> connectionToSource; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectsource.cpp b/src/remoteobjects/qremoteobjectsource.cpp new file mode 100644 index 0000000..ca03e18 --- /dev/null +++ b/src/remoteobjects/qremoteobjectsource.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteobjectsource.h" +#include "qremoteobjectsource_p.h" + +#include "qconnectionabstractserver_p.h" +#include "qtremoteobjectglobal.h" + +#include <QMetaProperty> +#include <QVarLengthArray> + +#include <algorithm> +#include <iterator> + +QT_BEGIN_NAMESPACE + +using namespace QRemoteObjectPackets; + +static QVector<int> parameter_types(const QMetaMethod &member) +{ + QVector<int> types; + types.reserve(member.parameterCount()); + for (int i = 0; i < member.parameterCount(); ++i) { + const int tp = member.parameterType(i); + if (tp == QMetaType::UnknownType) { + Q_ASSERT(tp != QMetaType::Void); // void parameter => metaobject is corrupt + qCWarning(QT_REMOTEOBJECT) <<"Don't know how to handle " + << member.parameterTypes().at(i).constData() + << ", use qRegisterMetaType to register it."; + + } + types << tp; + } + return types; +} + +QRemoteObjectSourcePrivate::QRemoteObjectSourcePrivate(QObject *obj, QMetaObject const *meta, const QString &name) + : QObject(obj), + args(meta->methodCount()), + m_name(name), + m_object(obj), + m_meta(meta), + m_methodOffset(meta == obj->metaObject() ? meta->methodOffset() : meta->methodCount()), + m_propertyOffset(meta == obj->metaObject() ? meta->propertyOffset() : meta->propertyCount()) +{ + if (!obj) { + qCWarning(QT_REMOTEOBJECT) << "QRemoteObjectSourcePrivate: Cannot replicate a NULL object" << name; + return; + } + + const QMetaObject *them = obj->metaObject(); + for (int idx = m_propertyOffset; idx < them->propertyCount(); ++idx) { + const QMetaProperty mp = them->property(idx); + qCDebug(QT_REMOTEOBJECT) << "Property option" << idx << mp.name() << mp.isWritable() << mp.hasNotifySignal() << mp.notifySignalIndex(); + if (mp.hasNotifySignal()) + propertyFromNotifyIndex.insert(mp.notifySignalIndex(), mp); + } + + args.reserve(them->methodCount() - m_methodOffset); + + for (int idx = m_methodOffset; idx < them->methodCount(); ++idx) { + const QMetaMethod mm = them->method(idx); + qCDebug(QT_REMOTEOBJECT) << "Connection option" << idx << mm.name(); + + if (mm.methodType() != QMetaMethod::Signal) + continue; + + // This basically connects the parent Signals (note, all dynamic properties have onChange + //notifications, thus signals) to us. Normally each Signal is mapped to a unique index, + //but since we are forwarding them all, we keep the offset constant. + // + //We know no one will inherit from this class, so no need to worry about indices from + //derived classes. + if (!QMetaObject::connect(obj, idx, this, m_methodOffset, Qt::DirectConnection, 0)) { + qCWarning(QT_REMOTEOBJECT) << "QRemoteObjectSourcePrivate: QMetaObject::connect returned false. Unable to connect."; + return; + } + + args.push_back(parameter_types(mm)); + + qCDebug(QT_REMOTEOBJECT) << "Connection made" << idx << mm.name(); + } +} + +QRemoteObjectSourcePrivate::~QRemoteObjectSourcePrivate() +{ + Q_FOREACH (ServerIoDevice *io, listeners) { + removeListener(io, true); + } +} + +QVariantList QRemoteObjectSourcePrivate::marshalArgs(int index, void **a) +{ + QVariantList list; + const QVector<int> &argsForIndex = args[index]; + const int N = argsForIndex.size(); + list.reserve(N); + for (int i = 0; i < N; ++i) { + const int type = argsForIndex[i]; + if (type == QMetaType::QVariant) + list << *reinterpret_cast<QVariant *>(a[i + 1]); + else + list << QVariant(type, a[i + 1]); + } + return list; +} + +void QRemoteObjectSourcePrivate::invoke(QMetaObject::Call c, int index, const QVariantList &args) +{ + static QVariant null(QMetaType::QObjectStar, Q_NULLPTR); + QVarLengthArray<void*, 10> param(args.size() + 1); + param[0] = null.data(); //Never a return value + for (int i = 0; i < args.size(); ++i) { + param[i + 1] = const_cast<void*>(args.at(i).data()); + } + if (c == QMetaObject::InvokeMetaMethod) { + parent()->qt_metacall(c, index + m_methodOffset, param.data()); + } else { + parent()->qt_metacall(c, index + m_propertyOffset, param.data()); + } +} + +#ifdef Q_COMPILER_UNIFORM_INIT +// QPair (like any class) can be initialized with { } +typedef QPair<QString,QVariant> Pair; +inline Pair make_pair(QString first, QVariant second) +{ return { qMove(first), qMove(second) }; } +#else +// QPair can't be initialized with { }, need to use a POD +struct Pair { + QString first; + QVariant second; +}; +inline QDataStream &operator<<(QDataStream &s, const Pair &p) +{ return s << p.first << p.second; } +inline Pair make_pair(QString first, QVariant second) +{ Pair p = { qMove(first), qMove(second) }; return p; } +#endif + +void QRemoteObjectSourcePrivate::handleMetaCall(int index, QMetaObject::Call call, void **a) +{ + if (listeners.empty()) + return; + + QByteArray ba; + + if (propertyFromNotifyIndex.contains(index)) { + const QMetaProperty mp = propertyFromNotifyIndex[index]; + qCDebug(QT_REMOTEOBJECT) << "Invoke Property" << mp.name() << mp.read(m_object); + QPropertyChangePacket p(m_name, mp.name(), mp.read(m_object)); + ba = p.serialize(); + } + + qCDebug(QT_REMOTEOBJECT) << "# Listeners" << listeners.length(); + qCDebug(QT_REMOTEOBJECT) << "Invoke args:" << m_object << call << index << marshalArgs(index, a); + QInvokePacket p(m_name, call, index - m_methodOffset, marshalArgs(index, a)); + + ba += p.serialize(); + + Q_FOREACH (ServerIoDevice *io, listeners) + io->write(ba); +} + +void QRemoteObjectSourcePrivate::addListener(ServerIoDevice *io, bool dynamic) +{ + listeners.append(io); + + if (dynamic) { + QRemoteObjectPackets::QInitDynamicPacketEncoder p(m_name, m_object, m_meta); + io->write(p.serialize()); + } else { + QRemoteObjectPackets::QInitPacketEncoder p(m_name, m_object, m_meta); + io->write(p.serialize()); + } +} + +int QRemoteObjectSourcePrivate::removeListener(ServerIoDevice *io, bool shouldSendRemove) +{ + listeners.removeAll(io); + if (shouldSendRemove) + { + QRemoveObjectPacket p(m_name); + io->write(p.serialize()); + } + return listeners.length(); +} + +int QRemoteObjectSourcePrivate::qt_metacall(QMetaObject::Call call, int methodId, void **a) +{ + //We get called from the stored metaobject metacall. Thus our index won't just be the index within our type, it will include + //an offset for any signals/slots in the meta base class. The delta offset accounts for this. + const int delta = m_meta->methodCount() - m_methodOffset; + + methodId = QObject::qt_metacall(call, methodId, a); + if (methodId < 0) + return methodId; + + if (call == QMetaObject::InvokeMetaMethod) { + if (methodId >= delta) { + handleMetaCall(senderSignalIndex(), call, a); + } + --methodId; + } + + return methodId; +} + + +QRemoteObjectSource::QRemoteObjectSource(QObject *parent) + : QObject(parent) + , d_ptr(Q_NULLPTR) +{ + emit initialized(); +} + +QRemoteObjectSource::~QRemoteObjectSource() +{ +} + +bool QRemoteObjectSource::isReplicaValid() const +{ + return true; +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectsource.h b/src/remoteobjects/qremoteobjectsource.h new file mode 100644 index 0000000..adf4a15 --- /dev/null +++ b/src/remoteobjects/qremoteobjectsource.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTSOURCE_H +#define QREMOTEOBJECTSOURCE_H + +#include "qtremoteobjectglobal.h" + +#include <QScopedPointer> + +QT_BEGIN_NAMESPACE + +class QRemoteObjectSourcePrivate; + +class Q_REMOTEOBJECTS_EXPORT QRemoteObjectSource : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool isReplicaValid READ isReplicaValid NOTIFY onIsReplicaValidChanged) + +public: + explicit QRemoteObjectSource(QObject *parent = Q_NULLPTR); + virtual ~QRemoteObjectSource(); + + bool isReplicaValid() const; + +Q_SIGNALS: + void onIsReplicaValidChanged(); + void initialized(); + +private: + friend class QRemoteObjectSourceIo; + Q_DECLARE_PRIVATE(QRemoteObjectSource) + + QScopedPointer<QRemoteObjectSourcePrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectsource_p.h b/src/remoteobjects/qremoteobjectsource_p.h new file mode 100644 index 0000000..3592034 --- /dev/null +++ b/src/remoteobjects/qremoteobjectsource_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTSOURCE_P_H +#define QREMOTEOBJECTSOURCE_P_H + +#include <QObject> + +#include <QVector> + +QT_BEGIN_NAMESPACE + +class ServerIoDevice; +class QRemoteObjectSource; + +template <typename Container> +class ContainerWithOffset { +public: + typedef typename Container::value_type value_type; + typedef typename Container::reference reference; + typedef typename Container::const_reference const_reference; + typedef typename Container::difference_type difference_type; + typedef typename Container::pointer pointer; + typedef typename Container::const_pointer const_pointer; + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + ContainerWithOffset() :c(), offset(0) {} + ContainerWithOffset(off_t offset) + : c(), offset(offset) {} + ContainerWithOffset(const Container &c, off_t offset) + : c(c), offset(offset) {} +#ifdef Q_COMPILER_RVALUE_REFS + ContainerWithOffset(Container &&c, off_t offset) + : c(std::move(c)), offset(offset) {} +#endif + + reference operator[](int i) { return c[i-offset]; }; + const_reference operator[](int i) const { return c[i-offset]; } + + void reserve(int size) { c.reserve(size); } + void push_back(const value_type &v) { c.push_back(v); } +#ifdef Q_COMPILER_RVALUE_REFS + void push_back(value_type &&v) { c.push_back(std::move(v)); } +#endif + +private: + Container c; + off_t offset; +}; + +class QRemoteObjectSourcePrivate : public QObject +{ +public: + explicit QRemoteObjectSourcePrivate(QObject *object, QMetaObject const *meta, const QString &name); + + ~QRemoteObjectSourcePrivate(); + + int qt_metacall(QMetaObject::Call call, int methodId, void **a); + ContainerWithOffset<QVector<QVector<int> > > args; + QHash<int, QMetaProperty> propertyFromNotifyIndex; + QList<ServerIoDevice*> listeners; + QString m_name; + QObject *m_object; + const QMetaObject * const m_meta; + const int m_methodOffset; + const int m_propertyOffset; + + QVariantList marshalArgs(int index, void **a); + void handleMetaCall(int index, QMetaObject::Call call, void **a); + void addListener(ServerIoDevice *io, bool dynamic = false); + int removeListener(ServerIoDevice *io, bool shouldSendRemove = false); + void invoke(QMetaObject::Call, int index, const QVariantList &args); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qremoteobjectsourceio.cpp b/src/remoteobjects/qremoteobjectsourceio.cpp new file mode 100644 index 0000000..51bd205 --- /dev/null +++ b/src/remoteobjects/qremoteobjectsourceio.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteobjectsourceio_p.h" + +#include "qremoteobjectsource.h" +#include "qremoteobjectsource_p.h" +#include "qtremoteobjectglobal.h" + +#include <QMetaClassInfo> +#include <QStringList> + +QT_BEGIN_NAMESPACE + +QRemoteObjectSourceIo::QRemoteObjectSourceIo(const QUrl &address) + : m_server(m_factory.createServer(address, this)) +{ + if (m_server->listen(address)) { + qCDebug(QT_REMOTEOBJECT) << "QRemoteObjectSourceIo is Listening" << address; + } else { + qCDebug(QT_REMOTEOBJECT) << "Listen failed"; + qCDebug(QT_REMOTEOBJECT) << address; + qCDebug(QT_REMOTEOBJECT) << m_server->serverError(); + } + + connect(m_server.data(), SIGNAL(newConnection()), this, SLOT(handleConnection())); + connect(&m_serverDelete, SIGNAL(mapped(QObject*)), this, SLOT(onServerDisconnect(QObject*))); + connect(&m_serverRead, SIGNAL(mapped(QObject*)), this, SLOT(onServerRead(QObject*))); + connect(&m_remoteObjectDestroyed, SIGNAL(mapped(QString)), this, SLOT(clearRemoteObjectSource(QString))); +} + +QRemoteObjectSourceIo::~QRemoteObjectSourceIo() +{ + Q_FOREACH (QRemoteObjectSourcePrivate *pp, m_remoteObjects) { + disableRemoting(pp); + } +} + +bool QRemoteObjectSourceIo::enableRemoting(QObject *object, const QMetaObject *meta, const QString &name) +{ + if (m_remoteObjects.contains(name)) { + qCWarning(QT_REMOTEOBJECT) << "Tried to register QRemoteObjectSource twice" << name; + return false; + } + + QRemoteObjectSourcePrivate *pp = new QRemoteObjectSourcePrivate(object, meta, name); + + qCDebug(QT_REMOTEOBJECT) << "Registering" << name; + + m_remoteObjects[name] = pp; + connect(pp, SIGNAL(destroyed()), &m_remoteObjectDestroyed, SLOT(map())); + m_remoteObjectDestroyed.setMapping(pp, name); + emit remoteObjectAdded(qMakePair(name, m_server->address())); + + return true; +} + +bool QRemoteObjectSourceIo::disableRemoting(QRemoteObjectSourcePrivate *pp) +{ + const QString name = pp->m_name; + clearRemoteObjectSource(name); + pp->setParent(Q_NULLPTR); + delete pp; + + return true; +} + +void QRemoteObjectSourceIo::onServerDisconnect(QObject *conn) +{ + ServerIoDevice *connection = qobject_cast<ServerIoDevice*>(conn); + m_connections.remove(connection); + + qCDebug(QT_REMOTEOBJECT) << "OnServerDisconnect"; + + Q_FOREACH (QRemoteObjectSourcePrivate *pp, m_remoteObjects) + pp->removeListener(connection); + + const QUrl location = m_registryMapping.value(connection); + emit serverRemoved(location); + m_registryMapping.remove(connection); + connection->close(); + connection->deleteLater(); +} + +void QRemoteObjectSourceIo::onServerRead(QObject *conn) +{ + // Assert the invariant here conn is of type QIODevice + ServerIoDevice *connection = qobject_cast<ServerIoDevice*>(conn); + + do { + + if (!connection->read()) + return; + + using namespace QRemoteObjectPackets; + + const QRemoteObjectPacket* packet = connection->packet(); + switch (packet->id) { + case QRemoteObjectPacket::AddObject: + { + const QAddObjectPacket *p = static_cast<const QAddObjectPacket *>(packet); + const QString name = p->name; + qCDebug(QT_REMOTEOBJECT) << "AddObject" << name << p->isDynamic; + if (m_remoteObjects.contains(name)) { + QRemoteObjectSourcePrivate *pp = m_remoteObjects[name]; + pp->addListener(connection, p->isDynamic); + } else { + qCWarning(QT_REMOTEOBJECT) << "Request to attach to non-existent RemoteObjectSource:" << name; + } + break; + } + case QRemoteObjectPacket::RemoveObject: + { + const QRemoveObjectPacket *p = static_cast<const QRemoveObjectPacket *>(packet); + const QString name = p->name; + qCDebug(QT_REMOTEOBJECT) << "RemoveObject" << name; + if (m_remoteObjects.contains(name)) { + QRemoteObjectSourcePrivate *pp = m_remoteObjects[name]; + const int count = pp->removeListener(connection); + Q_UNUSED(count); + //TODO - possible to have a timer that closes connections if not reopened within a timeout? + } else { + qCWarning(QT_REMOTEOBJECT) << "Request to detach from non-existent RemoteObjectSource:" << name; + } + qCDebug(QT_REMOTEOBJECT) << "RemoveObject finished" << name; + break; + } + case QRemoteObjectPacket::InvokePacket: + { + const QInvokePacket *p = static_cast<const QInvokePacket *>(packet); + const QString name = p->name; + if (name == QStringLiteral("Registry") && !m_registryMapping.contains(connection)) { + const QRemoteObjectSourceLocation loc = p->args.first().value<QRemoteObjectSourceLocation>(); + m_registryMapping[connection] = loc.second; + } + if (m_remoteObjects.contains(name)) { + QRemoteObjectSourcePrivate *pp = m_remoteObjects[name]; + if (p->call == QMetaObject::InvokeMetaMethod) { + qCDebug(QT_REMOTEOBJECT) << "Source (method) Invoke-->" << name << pp->m_meta->method(p->index+pp->m_methodOffset).name(); + pp->invoke(QMetaObject::InvokeMetaMethod, p->index, p->args); + } else { + qCDebug(QT_REMOTEOBJECT) << "Source (write property) Invoke-->" << name << pp->m_meta->property(p->index+pp->m_propertyOffset).name(); + pp->invoke(QMetaObject::WriteProperty, p->index, p->args); + } + } + break; + } + default: + qCDebug(QT_REMOTEOBJECT) << "OnReadReady invalid type" << packet->id; + } + } while (connection->bytesAvailable()); // have bytes left over, so do another iteration +} + +void QRemoteObjectSourceIo::handleConnection() +{ + qCDebug(QT_REMOTEOBJECT) << "handleConnection" << m_connections; + + ServerIoDevice *conn = m_server->nextPendingConnection(); + m_connections.insert(conn); + connect(conn, SIGNAL(disconnected()), &m_serverDelete, SLOT(map())); + m_serverDelete.setMapping(conn, conn); + connect(conn, SIGNAL(readyRead()), &m_serverRead, SLOT(map())); + m_serverRead.setMapping(conn, conn); + + QRemoteObjectPackets::QObjectListPacket p(QStringList(m_remoteObjects.keys())); + conn->write(p.serialize()); + qCDebug(QT_REMOTEOBJECT) << "Wrote ObjectList packet from Server"; +} + +void QRemoteObjectSourceIo::clearRemoteObjectSource(const QString &name) +{ + m_remoteObjects.remove(name); + emit remoteObjectRemoved(qMakePair(name, serverAddress())); +} + +QUrl QRemoteObjectSourceIo::serverAddress() const +{ + return m_server->address(); +} + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qremoteobjectsourceio_p.h b/src/remoteobjects/qremoteobjectsourceio_p.h new file mode 100644 index 0000000..88ac68d --- /dev/null +++ b/src/remoteobjects/qremoteobjectsourceio_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTEOBJECTSOURCEIO_P_H +#define QREMOTEOBJECTSOURCEIO_P_H + +#include "qconnectionserverfactory_p.h" +#include "qtremoteobjectglobal.h" + +#include <QIODevice> +#include <QScopedPointer> +#include <QSignalMapper> + +QT_BEGIN_NAMESPACE + +class QRemoteObjectSource; +class QRemoteObjectSourcePrivate; + +class QRemoteObjectSourceIo : public QObject +{ + Q_OBJECT +public: + explicit QRemoteObjectSourceIo(const QUrl &address); + ~QRemoteObjectSourceIo(); + + bool enableRemoting(QObject *object, const QMetaObject *meta, const QString &name); + bool disableRemoting(QRemoteObjectSourcePrivate *pp); + + QUrl serverAddress() const; + +public Q_SLOTS: + void handleConnection(); + void onServerDisconnect(QObject *obj = Q_NULLPTR); + void onServerRead(QObject *obj); + void clearRemoteObjectSource(const QString &name); + +Q_SIGNALS: + void remoteObjectAdded(const QRemoteObjectSourceLocation &); + void remoteObjectRemoved(const QRemoteObjectSourceLocation &); + void serverRemoved(const QUrl& url); + +private: + friend class QRemoteObjectNodePrivate; + QHash<QIODevice*, quint32> m_readSize; + QConnectionServerFactory m_factory; + QSet<ServerIoDevice*> m_connections; + QMap<QString, QRemoteObjectSourcePrivate*> m_remoteObjects; + QSignalMapper m_serverDelete; + QSignalMapper m_serverRead; + QSignalMapper m_remoteObjectDestroyed; + QHash<ServerIoDevice*, QUrl> m_registryMapping; + QScopedPointer<QConnectionAbstractServer> m_server; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/remoteobjects/qtremoteobjectglobal.cpp b/src/remoteobjects/qtremoteobjectglobal.cpp new file mode 100644 index 0000000..ce350de --- /dev/null +++ b/src/remoteobjects/qtremoteobjectglobal.cpp @@ -0,0 +1,526 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtremoteobjectglobal.h" + +#include <QMetaObject> +#include <QMetaProperty> +#include "private/qmetaobjectbuilder_p.h" + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(QT_REMOTEOBJECT, "qt.remoteobjects") + +namespace QRemoteObjectPackets { + +QRemoteObjectPacket::~QRemoteObjectPacket(){} + +QRemoteObjectPacket *QRemoteObjectPacket::fromDataStream(QDataStream &in) +{ + QRemoteObjectPacket *packet = Q_NULLPTR; + quint16 type; + in >> type; + switch (type) { + case InitPacket: + packet = new QInitPacket; + if (packet->deserialize(in)) + packet->id = InitPacket; + break; + case InitDynamicPacket: + packet = new QInitDynamicPacket; + if (packet->deserialize(in)) + packet->id = InitDynamicPacket; + break; + case AddObject: + packet = new QAddObjectPacket; + if (packet->deserialize(in)) + packet->id = AddObject; + break; + case RemoveObject: + packet = new QRemoveObjectPacket; + if (packet->deserialize(in)) + packet->id = RemoveObject; + break; + case InvokePacket: + packet = new QInvokePacket; + if (packet->deserialize(in)) + packet->id = InvokePacket; + break; + case PropertyChangePacket: + packet = new QPropertyChangePacket; + if (packet->deserialize(in)) + packet->id = PropertyChangePacket; + break; + case ObjectList: + packet = new QObjectListPacket; + if (packet->deserialize(in)) + packet->id = ObjectList; + break; + default: + qWarning() << "Invalid packet received" << type; + } + return packet; +} + +QByteArray QInitPacketEncoder::serialize() const +{ + DataStreamPacket ds(id); + ds << name; + qint64 postNamePosition = ds.device()->pos(); + ds << quint32(0); + + //Now copy the property data + const QMetaObject *meta = object->metaObject(); + const int propertyOffset = base && base != meta ? base->propertyCount() : meta->propertyOffset(); + const int nParam = meta->propertyCount(); + + ds << quint32(nParam - propertyOffset); //Number of properties + + for (int i = propertyOffset; i < nParam; ++i) { + const QMetaProperty mp = meta->property(i); + ds << mp.name(); + ds << mp.read(object); + } + + //Now go back and set the size of the rest of the data so we can treat is as a QByteArray + ds.device()->seek(postNamePosition); + ds << quint32(ds.array.length() - sizeof(quint32) - postNamePosition); + return ds.finishPacket(); +} + +bool QInitPacketEncoder::deserialize(QDataStream &) +{ + Q_ASSERT(false); //Use QInitPacket::deserialize() + return false; +} + +QByteArray QInitPacket::serialize() const +{ + Q_ASSERT(false); //Use QInitPacketEncoder::serialize() + return QByteArray(); +} + +bool QInitPacket::deserialize(QDataStream& in) +{ + if (in.atEnd()) + return false; + in >> name; + if (name.isEmpty() || name.isNull() || in.atEnd()) + return false; + in >> packetData; + if (packetData.isEmpty() || packetData.isNull()) + return false; + + //Make sure the bytearray holds valid properties + QDataStream validate(packetData); + const int packetLen = packetData.size(); + quint32 nParam, len; + quint8 c; + QVariant tmp; + validate >> nParam; + for (quint32 i = 0; i < nParam; i++) + { + const qint64 pos = validate.device()->pos(); + qint64 bytesLeft = packetLen - pos; + if (bytesLeft < 4) + return false; + validate >> len; + bytesLeft -= 4; + if (bytesLeft < len) + return false; + validate.skipRawData(len-1); + validate >> c; + if (c != 0) + return false; + if (qstrlen(packetData.constData()+pos+4) != len - 1) + return false; + bytesLeft -= len; + if (bytesLeft <= 0) + return false; + validate >> tmp; + if (!tmp.isValid()) + return false; + } + return true; +} + +QByteArray QInitDynamicPacketEncoder::serialize() const +{ + DataStreamPacket ds(id); + ds << name; + qint64 postNamePosition = ds.device()->pos(); + ds << quint32(0); + + //Now copy the property data + const QMetaObject *meta = object->metaObject(); + const int propertyOffset = base && base != meta ? base->propertyCount() : meta->propertyOffset(); + const int nParam = meta->propertyCount(); + const int methodOffset = base && base != meta ? base->methodCount() : meta->methodOffset(); + const int numMethods = meta->methodCount(); + + ds << quint32(numMethods - methodOffset); + for (int i = methodOffset; i < numMethods; ++i) { + const QMetaMethod mm = meta->method(i); + ds << quint32(i - methodOffset); + ds << mm.name(); + ds << mm.methodSignature(); + ds << quint32(mm.methodType()); + if (mm.methodType() == QMetaMethod::Method) + ds << mm.typeName(); + ds << quint32(mm.parameterCount()); + for (int i = 0; i < mm.parameterCount(); ++i) + ds << mm.parameterTypes()[i]; + } + + ds << quint32(nParam - propertyOffset); //Number of properties + + for (int i = propertyOffset; i < nParam; ++i) { + const QMetaProperty mp = meta->property(i); + ds << mp.name(); + ds << mp.typeName(); + if (mp.notifySignalIndex() == -1) + ds << QByteArray(); + else + ds << mp.notifySignal().methodSignature(); + ds << mp.read(object); + } + + //Now go back and set the size of the rest of the data so we can treat is as a QByteArray + ds.device()->seek(postNamePosition); + ds << quint32(ds.array.length() - sizeof(quint32) - postNamePosition); + return ds.finishPacket(); +} + +bool QInitDynamicPacketEncoder::deserialize(QDataStream &) +{ + Q_ASSERT(false); //Use QInitDynamicPacket::deserialize() + return false; +} + +QByteArray QInitDynamicPacket::serialize() const +{ + Q_ASSERT(false); //Use QInitDynamicPacketEncoder::serialize() + return QByteArray(); +} + +bool QInitDynamicPacket::deserialize(QDataStream& in) +{ + if (in.atEnd()) + return false; + in >> name; + if (name.isEmpty() || name.isNull() || in.atEnd()) + return false; + in >> packetData; + if (packetData.isEmpty() || packetData.isNull()) + return false; + + //Make sure the bytearray holds valid properties // TODO maybe really evaluate + return true; + QDataStream validate(packetData); + int packetLen = packetData.size(); + quint32 nParam, len, propLen; + quint8 c; + QVariant tmp; + validate >> nParam; + for (quint32 i = 0; i < nParam; i++) + { + qint64 pos = validate.device()->pos(); + qint64 bytesLeft = packetLen - pos; + if (bytesLeft < 4) + return false; + + //Read property name + validate >> len; + bytesLeft -= 4; + if (bytesLeft < len) + return false; + validate.skipRawData(len-1); + validate >> c; + if (c != 0) + return false; + if (qstrlen(packetData.constData()+pos+4) != len - 1) + return false; + bytesLeft -= len; + if (bytesLeft <= 0) + return false; + + //Read notify name + propLen = len; + validate >> len; + bytesLeft -= 4; + if (bytesLeft < len) + return false; + if (len) { //notify isn't empty + validate.skipRawData(len-1); + validate >> c; + if (c != 0) + return false; + if (qstrlen(packetData.constData()+pos+4+propLen+4) != len - 1) + return false; + bytesLeft -= len; + } + if (bytesLeft <= 0) + return false; + + //Read QVariant value + validate >> tmp; + if (!tmp.isValid()) + return false; + } + return true; +} + +QMetaObject *QInitDynamicPacket::createMetaObject(QMetaObjectBuilder &builder, + QVector<int> &methodTypes, + QVector<QVector<int> > &methodArgumentTypes, + QVector<QPair<QByteArray, QVariant> > *propertyValues) const +{ + quint32 numMethods = 0; + QDataStream ds(packetData); + ds >> numMethods; + methodTypes.clear(); + methodTypes.resize(numMethods); + methodArgumentTypes.clear(); + methodArgumentTypes.resize(numMethods); + for (quint32 i = 0; i < numMethods; ++i) { + quint32 index = 0; + QMetaMethod::MethodType type; + QByteArray name; + QByteArray signature; + QByteArray returnType; + quint32 typeValue; + + ds >> index; + ds >> name; + ds >> signature; + ds >> typeValue; + + type = static_cast<QMetaMethod::MethodType>(typeValue); + methodTypes[i] = type; + if (typeValue == QMetaMethod::Method) + ds >> returnType; + quint32 parameterCount = 0; + ds >> parameterCount; + QByteArray parameterType; + methodArgumentTypes[index].reserve(parameterCount); + for (unsigned int pCount = 0; pCount < parameterCount; ++pCount) { + ds >> parameterType; + methodArgumentTypes[index] << QVariant::nameToType(parameterType.constData()); + } + if (type == QMetaMethod::Signal) + builder.addSignal(signature); + else if (type == QMetaMethod::Slot) + builder.addMethod(signature); + else + builder.addMethod(signature, returnType); + } + + quint32 numProperties = 0; + ds >> numProperties; //Number of properties + + QVector<QPair<QByteArray, QVariant> > &propVal = *propertyValues; + for (unsigned int i = 0; i < numProperties; ++i) { + QByteArray name; + QByteArray typeName; + QByteArray signalName; + ds >> name; + ds >> typeName; + ds >> signalName; + if (signalName.isEmpty()) + builder.addProperty(name, typeName); + else + builder.addProperty(name, typeName, builder.indexOfSignal(signalName)); + QVariant value; + ds >> value; + propVal.append(qMakePair(name, value)); + } + + return builder.toMetaObject(); +} + + +QByteArray QAddObjectPacket::serialize() const +{ + DataStreamPacket ds(id); + ds << name; + ds << isDynamic; + return ds.finishPacket(); +} + +bool QAddObjectPacket::deserialize(QDataStream& in) +{ + in >> name; + in >> isDynamic; + return true; +} + +QByteArray QRemoveObjectPacket::serialize() const +{ + DataStreamPacket ds(id); + ds << name; + return ds.finishPacket(); +} + +bool QRemoveObjectPacket::deserialize(QDataStream& in) +{ + in >> name; + return true; +} + +QByteArray QInvokePacket::serialize() const +{ + DataStreamPacket ds(id); + ds << name; + ds << call; + ds << index; + ds << args; + return ds.finishPacket(); +} + +bool QInvokePacket::deserialize(QDataStream& in) +{ + in >> name; + in >> call; + in >> index; + in >> args; + return true; +} + +QByteArray QPropertyChangePacket::serialize() const +{ + DataStreamPacket ds(id); + ds << name; + ds << propertyName; + ds << value; + return ds.finishPacket(); +} + +bool QPropertyChangePacket::deserialize(QDataStream& in) +{ + in >> name; + in >> propertyName; + in >> value; + return true; +} + +QByteArray QObjectListPacket::serialize() const +{ + DataStreamPacket ds(id); + ds << objects; + return ds.finishPacket(); +} + +bool QObjectListPacket::deserialize(QDataStream& in) +{ + in >> objects; + return true; +} + +} + +namespace QtRemoteObjects { + +void copyStoredProperties(const QObject *src, QObject *dst) +{ + if (!src) { + qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy from a null QObject"; + return; + } + if (!dst) { + qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy to a null QObject"; + return; + } + + const QMetaObject * const mof = src->metaObject(); + const QMetaObject * const mot = dst->metaObject(); + if (mof->propertyCount() != mot->propertyCount()) { + qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy from a different QObject"; + return; + } + + for (int i = 0, end = mof->propertyCount(); i != end; ++i) { + const QMetaProperty mpf = mof->property(i); + if (!mpf.isStored(src)) + continue; + const QMetaProperty mpt = mot->property(i); + mpt.write(dst, mpf.read(src)); + } +} + +void copyStoredProperties(const QObject *src, QDataStream &dst) +{ + if (!src) { + qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy from a null QObject"; + return; + } + + const QMetaObject * const mof = src->metaObject(); + + for (int i = 0, end = mof->propertyCount(); i != end; ++i) { + const QMetaProperty mpf = mof->property(i); + if (!mpf.isStored(src)) + continue; + dst << mpf.read(src); + } +} + +void copyStoredProperties(QDataStream &src, QObject *dst) +{ + if (!dst) { + qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy to a null QObject"; + return; + } + + const QMetaObject * const mot = dst->metaObject(); + + for (int i = 0, end = mot->propertyCount(); i != end; ++i) { + const QMetaProperty mpt = mot->property(i); + if (!mpt.isStored(dst)) + continue; + QVariant v; + src >> v; + mpt.write(dst, v); + } +} + +} // namespace QtRemoteObjects + +QT_END_NAMESPACE diff --git a/src/remoteobjects/qtremoteobjectglobal.h b/src/remoteobjects/qtremoteobjectglobal.h new file mode 100644 index 0000000..53442f0 --- /dev/null +++ b/src/remoteobjects/qtremoteobjectglobal.h @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Ford Motor Company +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTREMOTEOBJECTGLOBAL_H +#define QTREMOTEOBJECTGLOBAL_H + +#include <QtCore/qglobal.h> +#include <QtCore/QHash> +#include <QtCore/QMap> +#include <QtCore/QPair> +#include <QtCore/QUrl> +#include <QtCore/QVariant> +#include <QtCore/QLoggingCategory> + +QT_BEGIN_NAMESPACE + +typedef QPair<QString, QUrl> QRemoteObjectSourceLocation; +typedef QHash<QString, QUrl> QRemoteObjectSourceLocations; + +Q_DECLARE_METATYPE(QRemoteObjectSourceLocation) +Q_DECLARE_METATYPE(QRemoteObjectSourceLocations) + +#ifndef QT_STATIC +# if defined(QT_BUILD_REMOTEOBJECTS_LIB) +# define Q_REMOTEOBJECTS_EXPORT Q_DECL_EXPORT +# else +# define Q_REMOTEOBJECTS_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_REMOTEOBJECTS_EXPORT +#endif + +#define QCLASSINFO_REMOTEOBJECT_TYPE "RemoteObject Type" + +class QDataStream; +class QMetaObjectBuilder; + +namespace QRemoteObjectStringLiterals { + +// when QStringLiteral is used with the same string in different functions, +// it creates duplicate static data. Wrapping it in inline functions prevents it. + +inline QString local() { return QStringLiteral("local"); } +inline QString tcp() { return QStringLiteral("tcp"); } + +} + +namespace QRemoteObjectPackets { + +//Helper class for creating a QByteArray from a QRemoteObjectPacket +class DataStreamPacket : public QDataStream +{ +public: + DataStreamPacket(quint16 id) : QDataStream(&array, QIODevice::WriteOnly) + { + *this << quint32(0); + *this << id; + } + QByteArray finishPacket() + { + this->device()->seek(0); + *this << quint32(array.length() - sizeof(quint32)); + return array; + } + QByteArray array; + +private: + Q_DISABLE_COPY(DataStreamPacket) +}; + +class QRemoteObjectPacket +{ +public: + enum QRemoteObjectPacketTypeEnum + { + Invalid = 0, + InitPacket, + InitDynamicPacket, + AddObject, + RemoveObject, + InvokePacket, + PropertyChangePacket, + ObjectList + }; + + QRemoteObjectPacket() { id = AddObject; } + virtual ~QRemoteObjectPacket(); + virtual QByteArray serialize() const = 0; + virtual bool deserialize(QDataStream&) = 0; + static QRemoteObjectPacket* fromDataStream(QDataStream&); + quint16 id; +}; + +class QInitPacketEncoder : public QRemoteObjectPacket +{ +public: + inline QInitPacketEncoder(const QString &_name, const QObject *_object, QMetaObject const *_base = Q_NULLPTR) : + name(_name), object(_object), base(_base) { id = InitPacket; } + QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + const QString name; + const QObject *object; + const QMetaObject *base; +private: + QInitPacketEncoder() {} +}; + +class QInitPacket : public QRemoteObjectPacket +{ +public: + inline QInitPacket() : QRemoteObjectPacket() {} + QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QString name; + QByteArray packetData; +}; + +class QInitDynamicPacketEncoder : public QRemoteObjectPacket +{ +public: + QInitDynamicPacketEncoder(const QString &_name, const QObject *_object, QMetaObject const *_base = Q_NULLPTR) : + name(_name), object(_object), base(_base) { id = InitDynamicPacket; } + QByteArray serialize() const Q_DECL_OVERRIDE; + bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + const QString name; + const QObject *object; + const QMetaObject *base; +private: + QInitDynamicPacketEncoder() {} +}; + +class QInitDynamicPacket : public QRemoteObjectPacket +{ +public: + inline QInitDynamicPacket() : QRemoteObjectPacket() {} + QByteArray serialize() const Q_DECL_OVERRIDE; + bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QMetaObject *createMetaObject(QMetaObjectBuilder &builder, + QVector<int> &methodTypes, + QVector<QVector<int> > &methodArgumentTypes, + QVector<QPair<QByteArray, QVariant> > *propertyValues = 0) const; + QString name; + QByteArray packetData; +}; + +class QAddObjectPacket : public QRemoteObjectPacket +{ +public: + inline QAddObjectPacket() : QRemoteObjectPacket(), + isDynamic(false) {} + inline QAddObjectPacket(const QString &_name, bool _isDynamic) : + name(_name), isDynamic(_isDynamic) { id = AddObject; } + virtual QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QString name; + bool isDynamic; +}; + +class QRemoveObjectPacket : public QRemoteObjectPacket +{ +public: + inline QRemoveObjectPacket() : QRemoteObjectPacket() {} + inline QRemoveObjectPacket(const QString &_name) : + name(_name) { id = RemoveObject; } + virtual QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QString name; +}; + +class QInvokePacket : public QRemoteObjectPacket +{ +public: + inline QInvokePacket() : QRemoteObjectPacket(), + call(-1), index(-1) {} + inline QInvokePacket(const QString &_name, int _call, int _index, QVariantList _args) : + name(_name), call(_call), index(_index), args(_args) { id = InvokePacket; } + virtual QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QString name; + int call; + int index; + QVariantList args; +}; + +class QPropertyChangePacket : public QRemoteObjectPacket +{ +public: + inline QPropertyChangePacket() : QRemoteObjectPacket() {} + inline QPropertyChangePacket(const QString &_name, const char *_propertyNameChar, const QVariant &_value) : + name(_name), propertyName(_propertyNameChar), value(_value) { id = PropertyChangePacket; } + virtual QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QString name; + QByteArray propertyName; + QVariant value; +}; + +class QObjectListPacket : public QRemoteObjectPacket +{ +public: + inline QObjectListPacket() : QRemoteObjectPacket() {} + inline QObjectListPacket(const QStringList &_objects) : + objects(_objects) { id = ObjectList; } + virtual QByteArray serialize() const Q_DECL_OVERRIDE; + virtual bool deserialize(QDataStream&) Q_DECL_OVERRIDE; + QStringList objects; +}; + +} // namespace QRemoteObjectPackets + +namespace QtRemoteObjects { + +Q_REMOTEOBJECTS_EXPORT void copyStoredProperties(const QObject *src, QObject *dst); +Q_REMOTEOBJECTS_EXPORT void copyStoredProperties(const QObject *src, QDataStream &dst); +Q_REMOTEOBJECTS_EXPORT void copyStoredProperties(QDataStream &src, QObject *dst); + +} + +Q_DECLARE_LOGGING_CATEGORY(QT_REMOTEOBJECT) + +QT_END_NAMESPACE + +#endif // QTREMOTEOBJECTSGLOBAL_H diff --git a/src/remoteobjects/remoteobjects.pro b/src/remoteobjects/remoteobjects.pro new file mode 100644 index 0000000..9e967c3 --- /dev/null +++ b/src/remoteobjects/remoteobjects.pro @@ -0,0 +1,51 @@ +TARGET = QtRemoteObjects +MODULE = remoteobjects +MODULE_CONFIG = remoteobjects_repc +QT += network core-private +QT -= gui + +QMAKE_DOCS = $$PWD/doc/qtremoteobjects.qdocconf + +load(qt_module) + +DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_BYTEARRAY QT_NO_URL_CAST_FROM_STRING + +INCLUDEPATH += $$PWD + +PUBLIC_HEADERS += \ + $$PWD/qremoteobjectdynamicreplica.h \ + $$PWD/qremoteobjectsource.h \ + $$PWD/qremoteobjectreplica.h \ + $$PWD/qremoteobjectnode.h \ + $$PWD/qtremoteobjectglobal.h \ + $$PWD/qremoteobjectregistry.h + +PRIVATE_HEADERS += \ + $$PWD/qconnectionabstractfactory_p.h \ + $$PWD/qconnectionabstractserver_p.h \ + $$PWD/qconnectionclientfactory_p.h \ + $$PWD/qconnectionserverfactory_p.h \ + $$PWD/qremoteobjectsourceio_p.h \ + $$PWD/qremoteobjectsource_p.h \ + $$PWD/qregistrysource_p.h \ + $$PWD/qremoteobjectnode_p.h \ + $$PWD/qremoteobjectreplica_p.h + +HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS + +SOURCES += \ + $$PWD/qconnectionabstractserver.cpp \ + $$PWD/qconnectionclientfactory.cpp \ + $$PWD/qconnectionserverfactory.cpp \ + $$PWD/qremoteobjectdynamicreplica.cpp \ + $$PWD/qremoteobjectsource.cpp \ + $$PWD/qremoteobjectsourceio.cpp \ + $$PWD/qremoteobjectregistry.cpp \ + $$PWD/qregistrysource.cpp \ + $$PWD/qremoteobjectreplica.cpp \ + $$PWD/qremoteobjectnode.cpp \ + $$PWD/qtremoteobjectglobal.cpp + +DEFINES += QT_BUILD_REMOTEOBJECTS_LIB + +contains(QT_CONFIG, c++11): CONFIG += c++11 diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 0000000..f16d105 --- /dev/null +++ b/src/src.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = remoteobjects |