qthighway/xqservice/src/xqservicechannel.cpp
branchRCL_3
changeset 9 5d007b20cfd0
--- /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 <xqserviceutil.h>
+#include <xqsharablefile.h>
+
+
+
+/*!
+    \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<XQServiceChannelPrivatePointer>());
+    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<XQServiceChannelPrivatePointer> 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<XQServiceChannelPrivatePointer> 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);
+    }
+}
+