diff -r 885c2596c964 -r 5d007b20cfd0 qthighway/xqservice/src/xqservicechannel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qthighway/xqservice/src/xqservicechannel.cpp Tue Aug 31 16:02:37 2010 +0300 @@ -0,0 +1,366 @@ +/* +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, version 2.1 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, +* see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/". +* +* Description: +* +*/ + +#include "xqservicelog.h" + +#include "xqservicechannel.h" + +#include "xqservicethreaddata.h" +#include "xqserviceipcclient.h" + +#include +#include + + + +/*! + \class XQServiceChannel + \inpublicgroup QtBaseModule + \ingroup qws + + \brief The XQServiceChannel class provides communication capabilities + between clients. + + XQSERVICE is a many-to-many communication protocol for transferring + messages on various channels. A channel is identified by a name, + and anyone who wants to can listen to it. The XQSERVICE protocol allows + clients to communicate both within the same address space and + between different processes, but it is currently only available + for \l {Qt for Embedded Linux} (on X11 and Windows we are exploring the use + of existing standards such as DCOP and COM). + + Typically, XQServiceChannel is either used to send messages to a + channel using the provided static functions, or to listen to the + traffic on a channel by deriving from the class to take advantage + of the provided functionality for receiving messages. + + XQServiceChannel provides a couple of static functions which are usable + without an object: The send() function, which sends the given + message and data on the specified channel, and the isRegistered() + function which queries the server for the existence of the given + channel. + + In addition, the XQServiceChannel class provides the channel() function + which returns the name of the object's channel, the virtual + receive() function which allows subclasses to process data + received from their channel, and the received() signal which is + emitted with the given message and data when a XQServiceChannel + subclass receives a message from its channel. + + \sa XQServiceServer, {Running Qt for Embedded Linux Applications} +*/ + +/*! + Constructs a XQService channel with the given \a parent, and registers it + with the server using the given \a channel name. + \param channel Channel name. + \param isServer + \param parent Parent of this object. + \sa isRegistered(), channel() +*/ + +XQServiceChannel::XQServiceChannel(const QString& channel, bool isServer, QObject *parent) + : QObject(parent) +{ + XQSERVICE_DEBUG_PRINT("XQServiceChannel::XQServiceChannel"); + XQSERVICE_DEBUG_PRINT("channel: %s, isServer: %d", qPrintable(channel), isServer ); + d = new XQServiceChannelPrivate(this, channel, isServer); + d->ref.ref(); + + XQServiceThreadData *td = XQService::serviceThreadData(); + + // do we need a new channel list ? + XQServiceClientMap::Iterator it = td->clientMap.find(channel); + if (it != td->clientMap.end()) { + XQSERVICE_DEBUG_PRINT("Channel exits"); + it.value().append(XQServiceChannelPrivatePointer(d)); + return; + } + XQSERVICE_DEBUG_PRINT("New channel"); + it = td->clientMap.insert(channel, QList()); + it.value().append(XQServiceChannelPrivatePointer(d)); +} + +/*! + Destroys the client's end of the channel and notifies the server + that the client has closed its connection. The server will keep + the channel open until the last registered client detaches. + + \sa XQServiceChannel() +*/ + +XQServiceChannel::~XQServiceChannel() +{ + XQSERVICE_DEBUG_PRINT("XQServiceChannel::~XQServiceChannel"); + XQServiceThreadData *td = XQService::serviceThreadData(); + + XQServiceClientMap::Iterator it = td->clientMap.find(d->channel); + Q_ASSERT(it != td->clientMap.end()); + it.value().removeAll(XQServiceChannelPrivatePointer(d)); + // still any clients connected locally ? + if (it.value().isEmpty()) { + if (td->hasClientConnection(d->channel)) + td->closeClientConnection(d->channel); + td->clientMap.remove(d->channel); + } + + // Dereference the private data structure. It may stay around + // for a little while longer if it is in use by sendLocally(). + d->object = 0; + if (!d->ref.deref()) + delete d; +} + +bool XQServiceChannel::connectChannel() +{ + XQSERVICE_DEBUG_PRINT("XQServiceChannel::connectChannel"); + bool ret = true; + + XQServiceThreadData *td = XQService::serviceThreadData(); + + if (!td->hasClientConnection(d->channel)) { + XQSERVICE_DEBUG_PRINT("Create new client connection (1)"); + ret = td->createClientConnection(d->channel,d->server); + } + return ret; +} + +/*! + Returns the name of the channel. + + \sa XQServiceChannel() +*/ + +QString XQServiceChannel::channel() const +{ + XQSERVICE_DEBUG_PRINT("XQServiceChannel::channel"); + XQSERVICE_DEBUG_PRINT("channel: %s", qPrintable(d->channel)); + return d->channel; +} + +/*! + \fn void XQServiceChannel::receive(const QString& message, const QByteArray &data) + + This virtual function allows subclasses of XQServiceChannel to process + the given \a message and \a data received from their channel. The default + implementation emits the received() signal. + + Note that the format of the given \a data has to be well defined + in order to extract the information it contains. In addition, it + is recommended to use the DCOP convention. This is not a + requirement, but you must ensure that the sender and receiver + agree on the argument types. + + Example: + + \code + void MyClass::receive(const QString &message, const QByteArray &data) + { + QDataStream in(data); + if (message == "execute(QString,QString)") { + QString cmd; + QString arg; + in >> cmd >> arg; + ... + } else if (message == "delete(QString)") { + QString fileName; + in >> fileName; + ... + } else { + ... + } + } + \endcode + + This example assumes that the \c message is a DCOP-style function + signature and the \c data contains the function's arguments. + + \sa send() + */ +QVariant XQServiceChannel::receive(const QString& msg, const QByteArray &data, const XQSharableFile &sf ) +{ + XQSERVICE_DEBUG_PRINT("XQServiceChannel::receive"); + XQSERVICE_DEBUG_PRINT("msg: %s, data: %s", qPrintable(msg), data.constData()); + emit received(msg, data,sf); + return QVariant(); +} + +void XQServiceChannel::commandReceive(const XQServiceCommand cmd) +{ + XQSERVICE_DEBUG_PRINT("XQServiceChannel::commandReceive %d", cmd); + emit commandReceived(cmd); +} + +/*! + \fn void XQServiceChannel::received(const QString& message, const QByteArray &data) + + This signal is emitted with the given \a message and \a data whenever the + receive() function gets incoming data. + + \sa receive() +*/ + +/*! + \fn bool XQServiceChannel::send(const QString& channel, const QString& message, + const QByteArray &data) + + Sends the given \a message on the specified \a channel with the + given \a data. The message will be distributed to all clients + subscribed to the channel. Returns true if the message is sent + successfully; otherwise returns false. + + It is recommended to use the DCOP convention. This is not a + requirement, but you must ensure that the sender and receiver + agree on the argument types. + + Note that QDataStream provides a convenient way to fill the byte + array with auxiliary data. + + Example: + + \code + QByteArray data; + QDataStream out(&data, QIODevice::WriteOnly); + out << QString("cat") << QString("file.txt"); + XQServiceChannel::send("System/Shell", "execute(QString,QString)", data); + \endcode + + Here the channel is \c "System/Shell". The \c message is an + arbitrary string, but in the example we've used the DCOP + convention of passing a function signature. Such a signature is + formatted as \c "functionname(types)" where \c types is a list of + zero or more comma-separated type names, with no whitespace, no + consts and no pointer or reference marks, i.e. no "*" or "&". + + \sa receive() +*/ + +bool XQServiceChannel::send(const QString& channel, const QString& msg, + const QByteArray &data, QVariant &retValue, + bool sync, XQServiceRequestCompletedAsync* rc) +{ + XQSERVICE_DEBUG_PRINT("XQServiceChannel::send(1). No user data"); + // Delegate to actual send. + // No user data argument present in this version + return send(channel,msg,data,retValue,sync,rc,NULL); +} + + + +bool XQServiceChannel::send(const QString& channel, const QString& msg, + const QByteArray &data, QVariant &retValue, + bool sync, XQServiceRequestCompletedAsync* rc, + const void* userData) +{ + XQSERVICE_DEBUG_PRINT("XQServiceChannel::send(2) start"); + XQSERVICE_DEBUG_PRINT("\t channel: %s, msg: %s", qPrintable(channel), qPrintable(msg)); + XQSERVICE_DEBUG_PRINT("\t data: %s, sync: %d", data.constData(), sync); + bool ret=true; + + if (!XQService::serviceThreadData()->hasClientConnection(channel)) { + XQSERVICE_DEBUG_PRINT("\t Create new client connection (2)"); + ret = XQService::serviceThreadData()->createClientConnection(channel,false,sync,rc, userData); + XQSERVICE_DEBUG_PRINT("\t creation succeeded: %d", ret); + } + if (ret) { + XQSERVICE_DEBUG_PRINT("\t ret = true"); + XQServiceIpcClient *cl = XQService::serviceThreadData()->clientConnection(channel); + QByteArray retData ; + ret = cl ? cl->send(channel, msg, data, retData) : false; + if (sync) { + retValue = XQServiceThreadData::deserializeRetData(retData); + } + } + XQSERVICE_DEBUG_PRINT("\t ret: %d", ret); + XQSERVICE_DEBUG_PRINT("XQServiceChannel::send(2) end"); + return ret; +} + + +bool XQServiceChannel::cancelPendingSend(const QString& channel) +{ + XQSERVICE_DEBUG_PRINT("XQServiceChannel::cancelPendingSend start"); + XQSERVICE_DEBUG_PRINT("\t channel: %s", qPrintable(channel)); + bool ret=true; + + if (ret) { + XQServiceIpcClient *cl = XQService::serviceThreadData()->clientConnection(channel); + XQSERVICE_DEBUG_PRINT("\t XQService::serviceThreadData()->clientConnection(channel): %d", cl); + XQSERVICE_DEBUG_PRINT("\t cl->cancelPendingSend(%s)", qPrintable(channel)); + ret = cl ? cl->cancelPendingSend(channel) : false; + } + + XQSERVICE_DEBUG_PRINT("\t ret: %d", ret); + XQSERVICE_DEBUG_PRINT("XQServiceChannel::cancelPendingSend end"); + return ret; +} + +/*! + \internal + Client side: distribute received event to the XQService instance managing the + channel. +*/ +QVariant XQServiceChannel::sendLocally(const QString& ch, const QString& msg, + const QByteArray &data, const XQSharableFile &sf ) +{ + XQSERVICE_DEBUG_PRINT("XQServiceChannel::sendLocally"); + XQSERVICE_DEBUG_PRINT("channel: %s, msg: %s", qPrintable(ch), qPrintable(msg)); + XQSERVICE_DEBUG_PRINT("data: %s", data.constData()); + QVariant ret; + + // feed local clients with received data + XQServiceThreadData *td = XQService::serviceThreadData(); + QList clients = td->clientMap[ch]; + for (int i = 0; i < clients.size(); ++i) { + XQServiceChannelPrivate *channel = clients.at(i).data(); + if (channel->object) + ret = channel->object->receive(msg, data,sf ); + } + +#ifdef XQSERVICE_DEBUG + QString s = ret.toString(); + int len=s.length(); + XQSERVICE_DEBUG_PRINT("sendLocally ret: type=%s,len=%d,value(max.1024)=%s", + ret.typeName(),len,qPrintable(s.left(1024))); +#endif + return ret ; +} + +int XQServiceChannel::latestError() +{ + XQSERVICE_DEBUG_PRINT("XQServiceChannel::latestError"); + return XQService::serviceThreadData()->latestError(); +} + +void XQServiceChannel::sendCommand(const QString& ch,const XQServiceCommand cmd) +{ + XQSERVICE_DEBUG_PRINT("XQServiceChannel::sendCommand"); + XQSERVICE_DEBUG_PRINT("channel: %s, cmd: %d", qPrintable(ch), cmd); + // feed local clients with received data + XQServiceThreadData *td = XQService::serviceThreadData(); + QList clients = td->clientMap[ch]; + for (int i = 0; i < clients.size(); ++i) { + XQServiceChannelPrivate *channel = clients.at(i).data(); + if (channel->object) + channel->object->commandReceive(cmd); + } +} +