qthighway/xqservice/src/xqservicechannel.cpp
branchRCL_3
changeset 9 5d007b20cfd0
equal deleted inserted replaced
8:885c2596c964 9:5d007b20cfd0
       
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 *
       
     5 * This program is free software: you can redistribute it and/or modify
       
     6 * it under the terms of the GNU Lesser General Public License as published by
       
     7 * the Free Software Foundation, version 2.1 of the License.
       
     8 * 
       
     9 * This program is distributed in the hope that it will be useful,
       
    10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12 * GNU Lesser General Public License for more details.
       
    13 *
       
    14 * You should have received a copy of the GNU Lesser General Public License
       
    15 * along with this program.  If not, 
       
    16 * see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/".
       
    17 *
       
    18 * Description:                                                         
       
    19 *
       
    20 */   
       
    21 
       
    22 #include "xqservicelog.h"
       
    23 
       
    24 #include "xqservicechannel.h"
       
    25 
       
    26 #include "xqservicethreaddata.h"
       
    27 #include "xqserviceipcclient.h"
       
    28 
       
    29 #include <xqserviceutil.h>
       
    30 #include <xqsharablefile.h>
       
    31 
       
    32 
       
    33 
       
    34 /*!
       
    35     \class XQServiceChannel
       
    36     \inpublicgroup QtBaseModule
       
    37     \ingroup qws
       
    38 
       
    39     \brief The XQServiceChannel class provides communication capabilities
       
    40     between clients.
       
    41 
       
    42     XQSERVICE is a many-to-many communication protocol for transferring
       
    43     messages on various channels. A channel is identified by a name,
       
    44     and anyone who wants to can listen to it. The XQSERVICE protocol allows
       
    45     clients to communicate both within the same address space and
       
    46     between different processes, but it is currently only available
       
    47     for \l {Qt for Embedded Linux} (on X11 and Windows we are exploring the use
       
    48     of existing standards such as DCOP and COM).
       
    49 
       
    50     Typically, XQServiceChannel is either used to send messages to a
       
    51     channel using the provided static functions, or to listen to the
       
    52     traffic on a channel by deriving from the class to take advantage
       
    53     of the provided functionality for receiving messages.
       
    54 
       
    55     XQServiceChannel provides a couple of static functions which are usable
       
    56     without an object: The send() function, which sends the given
       
    57     message and data on the specified channel, and the isRegistered()
       
    58     function which queries the server for the existence of the given
       
    59     channel.
       
    60 
       
    61     In addition, the XQServiceChannel class provides the channel() function
       
    62     which returns the name of the object's channel, the virtual
       
    63     receive() function which allows subclasses to process data
       
    64     received from their channel, and the received() signal which is
       
    65     emitted with the given message and data when a XQServiceChannel
       
    66     subclass receives a message from its channel.
       
    67 
       
    68     \sa XQServiceServer, {Running Qt for Embedded Linux Applications}
       
    69 */
       
    70 
       
    71 /*!
       
    72     Constructs a XQService channel with the given \a parent, and registers it
       
    73     with the server using the given \a channel name.
       
    74     \param channel Channel name.
       
    75     \param isServer 
       
    76     \param parent Parent of this object.
       
    77     \sa isRegistered(), channel()
       
    78 */
       
    79 
       
    80 XQServiceChannel::XQServiceChannel(const QString& channel, bool isServer, QObject *parent)
       
    81     : QObject(parent)
       
    82 {
       
    83     XQSERVICE_DEBUG_PRINT("XQServiceChannel::XQServiceChannel");
       
    84     XQSERVICE_DEBUG_PRINT("channel: %s, isServer: %d", qPrintable(channel), isServer );
       
    85     d = new XQServiceChannelPrivate(this, channel, isServer);
       
    86     d->ref.ref();
       
    87 
       
    88     XQServiceThreadData *td = XQService::serviceThreadData();
       
    89 
       
    90     // do we need a new channel list ?
       
    91     XQServiceClientMap::Iterator it = td->clientMap.find(channel);
       
    92     if (it != td->clientMap.end()) {
       
    93         XQSERVICE_DEBUG_PRINT("Channel exits");
       
    94         it.value().append(XQServiceChannelPrivatePointer(d));
       
    95         return;
       
    96     }
       
    97     XQSERVICE_DEBUG_PRINT("New channel");
       
    98     it = td->clientMap.insert(channel, QList<XQServiceChannelPrivatePointer>());
       
    99     it.value().append(XQServiceChannelPrivatePointer(d));
       
   100 }
       
   101 
       
   102 /*!
       
   103     Destroys the client's end of the channel and notifies the server
       
   104     that the client has closed its connection. The server will keep
       
   105     the channel open until the last registered client detaches.
       
   106 
       
   107     \sa XQServiceChannel()
       
   108 */
       
   109 
       
   110 XQServiceChannel::~XQServiceChannel()
       
   111 {
       
   112     XQSERVICE_DEBUG_PRINT("XQServiceChannel::~XQServiceChannel");
       
   113     XQServiceThreadData *td = XQService::serviceThreadData();
       
   114 
       
   115     XQServiceClientMap::Iterator it = td->clientMap.find(d->channel);
       
   116     Q_ASSERT(it != td->clientMap.end());
       
   117     it.value().removeAll(XQServiceChannelPrivatePointer(d));
       
   118     // still any clients connected locally ?
       
   119     if (it.value().isEmpty()) {
       
   120         if (td->hasClientConnection(d->channel))
       
   121             td->closeClientConnection(d->channel);
       
   122         td->clientMap.remove(d->channel);
       
   123     }
       
   124 
       
   125     // Dereference the private data structure.  It may stay around
       
   126     // for a little while longer if it is in use by sendLocally().
       
   127     d->object = 0;
       
   128     if (!d->ref.deref())
       
   129         delete d;
       
   130 }
       
   131 
       
   132 bool XQServiceChannel::connectChannel()
       
   133 {
       
   134     XQSERVICE_DEBUG_PRINT("XQServiceChannel::connectChannel");
       
   135     bool ret = true;
       
   136 
       
   137     XQServiceThreadData *td = XQService::serviceThreadData();
       
   138     
       
   139     if (!td->hasClientConnection(d->channel)) {
       
   140         XQSERVICE_DEBUG_PRINT("Create new client connection (1)");
       
   141         ret = td->createClientConnection(d->channel,d->server);
       
   142     }
       
   143     return ret;
       
   144 }
       
   145 
       
   146 /*!
       
   147     Returns the name of the channel.
       
   148 
       
   149     \sa XQServiceChannel()
       
   150 */
       
   151 
       
   152 QString XQServiceChannel::channel() const
       
   153 {
       
   154     XQSERVICE_DEBUG_PRINT("XQServiceChannel::channel");
       
   155     XQSERVICE_DEBUG_PRINT("channel: %s", qPrintable(d->channel));
       
   156     return d->channel;
       
   157 }
       
   158 
       
   159 /*!
       
   160     \fn void XQServiceChannel::receive(const QString& message, const QByteArray &data)
       
   161 
       
   162     This virtual function allows subclasses of XQServiceChannel to process
       
   163     the given \a message and \a data received from their channel. The default
       
   164     implementation emits the received() signal.
       
   165 
       
   166     Note that the format of the given \a data has to be well defined
       
   167     in order to extract the information it contains. In addition, it
       
   168     is recommended to use the DCOP convention. This is not a
       
   169     requirement, but you must ensure that the sender and receiver
       
   170     agree on the argument types.
       
   171 
       
   172     Example:
       
   173 
       
   174     \code
       
   175         void MyClass::receive(const QString &message, const QByteArray &data)
       
   176         {
       
   177             QDataStream in(data);
       
   178             if (message == "execute(QString,QString)") {
       
   179                 QString cmd;
       
   180                 QString arg;
       
   181                 in >> cmd >> arg;
       
   182                 ...
       
   183             } else if (message == "delete(QString)") {
       
   184                 QString fileName;
       
   185                 in >> fileName;
       
   186                 ...
       
   187             } else {
       
   188                 ...
       
   189             }
       
   190         }
       
   191     \endcode
       
   192 
       
   193     This example assumes that the \c message is a DCOP-style function
       
   194     signature and the \c data contains the function's arguments.
       
   195 
       
   196     \sa send()
       
   197  */
       
   198 QVariant XQServiceChannel::receive(const QString& msg, const QByteArray &data, const XQSharableFile &sf )
       
   199 {
       
   200     XQSERVICE_DEBUG_PRINT("XQServiceChannel::receive");
       
   201     XQSERVICE_DEBUG_PRINT("msg: %s, data: %s", qPrintable(msg), data.constData());
       
   202     emit received(msg, data,sf);
       
   203     return QVariant();
       
   204 }
       
   205 
       
   206 void XQServiceChannel::commandReceive(const XQServiceCommand cmd)
       
   207 {
       
   208     XQSERVICE_DEBUG_PRINT("XQServiceChannel::commandReceive %d", cmd);
       
   209     emit commandReceived(cmd);
       
   210 }
       
   211 
       
   212 /*!
       
   213     \fn void XQServiceChannel::received(const QString& message, const QByteArray &data)
       
   214 
       
   215     This signal is emitted with the given \a message and \a data whenever the
       
   216     receive() function gets incoming data.
       
   217 
       
   218     \sa receive()
       
   219 */
       
   220 
       
   221 /*!
       
   222     \fn bool XQServiceChannel::send(const QString& channel, const QString& message,
       
   223                        const QByteArray &data)
       
   224 
       
   225     Sends the given \a message on the specified \a channel with the
       
   226     given \a data.  The message will be distributed to all clients
       
   227     subscribed to the channel. Returns true if the message is sent
       
   228     successfully; otherwise returns false.
       
   229 
       
   230     It is recommended to use the DCOP convention. This is not a
       
   231     requirement, but you must ensure that the sender and receiver
       
   232     agree on the argument types.
       
   233 
       
   234     Note that QDataStream provides a convenient way to fill the byte
       
   235     array with auxiliary data.
       
   236 
       
   237     Example:
       
   238 
       
   239     \code
       
   240         QByteArray data;
       
   241         QDataStream out(&data, QIODevice::WriteOnly);
       
   242         out << QString("cat") << QString("file.txt");
       
   243         XQServiceChannel::send("System/Shell", "execute(QString,QString)", data);
       
   244     \endcode
       
   245 
       
   246     Here the channel is \c "System/Shell". The \c message is an
       
   247     arbitrary string, but in the example we've used the DCOP
       
   248     convention of passing a function signature. Such a signature is
       
   249     formatted as \c "functionname(types)" where \c types is a list of
       
   250     zero or more comma-separated type names, with no whitespace, no
       
   251     consts and no pointer or reference marks, i.e. no "*" or "&".
       
   252 
       
   253     \sa receive()
       
   254 */
       
   255 
       
   256 bool XQServiceChannel::send(const QString& channel, const QString& msg,
       
   257                             const QByteArray &data, QVariant &retValue,
       
   258                             bool sync, XQServiceRequestCompletedAsync* rc)
       
   259 {
       
   260     XQSERVICE_DEBUG_PRINT("XQServiceChannel::send(1). No user data");
       
   261     // Delegate to actual send.
       
   262     // No user data argument present in this version
       
   263     return send(channel,msg,data,retValue,sync,rc,NULL);
       
   264 }
       
   265 
       
   266 
       
   267 
       
   268 bool XQServiceChannel::send(const QString& channel, const QString& msg,
       
   269                        const QByteArray &data, QVariant &retValue,
       
   270                        bool sync, XQServiceRequestCompletedAsync* rc,
       
   271                        const void* userData)
       
   272 {
       
   273     XQSERVICE_DEBUG_PRINT("XQServiceChannel::send(2) start");
       
   274     XQSERVICE_DEBUG_PRINT("\t channel: %s, msg: %s", qPrintable(channel), qPrintable(msg));
       
   275     XQSERVICE_DEBUG_PRINT("\t data: %s, sync: %d", data.constData(), sync);
       
   276     bool ret=true;
       
   277     
       
   278     if (!XQService::serviceThreadData()->hasClientConnection(channel)) {
       
   279         XQSERVICE_DEBUG_PRINT("\t Create new client connection (2)");
       
   280         ret = XQService::serviceThreadData()->createClientConnection(channel,false,sync,rc, userData);
       
   281         XQSERVICE_DEBUG_PRINT("\t creation succeeded: %d", ret);
       
   282     }
       
   283     if (ret) {
       
   284         XQSERVICE_DEBUG_PRINT("\t ret = true");
       
   285         XQServiceIpcClient *cl = XQService::serviceThreadData()->clientConnection(channel);
       
   286         QByteArray retData ;
       
   287         ret = cl ? cl->send(channel, msg, data, retData) : false;
       
   288         if (sync) {
       
   289             retValue = XQServiceThreadData::deserializeRetData(retData);
       
   290         }
       
   291     }
       
   292     XQSERVICE_DEBUG_PRINT("\t ret: %d", ret);
       
   293 	XQSERVICE_DEBUG_PRINT("XQServiceChannel::send(2) end");
       
   294     return ret;
       
   295 }
       
   296 
       
   297 
       
   298 bool XQServiceChannel::cancelPendingSend(const QString& channel)
       
   299 {
       
   300     XQSERVICE_DEBUG_PRINT("XQServiceChannel::cancelPendingSend start");
       
   301     XQSERVICE_DEBUG_PRINT("\t channel: %s", qPrintable(channel));
       
   302     bool ret=true;
       
   303     
       
   304     if (ret) {
       
   305         XQServiceIpcClient *cl = XQService::serviceThreadData()->clientConnection(channel);
       
   306         XQSERVICE_DEBUG_PRINT("\t XQService::serviceThreadData()->clientConnection(channel): %d", cl);
       
   307         XQSERVICE_DEBUG_PRINT("\t cl->cancelPendingSend(%s)", qPrintable(channel));
       
   308         ret = cl ? cl->cancelPendingSend(channel) : false;
       
   309     }
       
   310     
       
   311     XQSERVICE_DEBUG_PRINT("\t ret: %d", ret);
       
   312 	XQSERVICE_DEBUG_PRINT("XQServiceChannel::cancelPendingSend end");
       
   313     return ret;
       
   314 }
       
   315 
       
   316 /*!
       
   317     \internal
       
   318     Client side: distribute received event to the XQService instance managing the
       
   319     channel.
       
   320 */
       
   321 QVariant XQServiceChannel::sendLocally(const QString& ch, const QString& msg,
       
   322                                 const QByteArray &data, const XQSharableFile &sf )
       
   323 {
       
   324     XQSERVICE_DEBUG_PRINT("XQServiceChannel::sendLocally");
       
   325     XQSERVICE_DEBUG_PRINT("channel: %s, msg: %s", qPrintable(ch), qPrintable(msg));
       
   326     XQSERVICE_DEBUG_PRINT("data: %s", data.constData());
       
   327     QVariant ret;
       
   328     
       
   329     // feed local clients with received data
       
   330     XQServiceThreadData *td = XQService::serviceThreadData();
       
   331     QList<XQServiceChannelPrivatePointer> clients = td->clientMap[ch];
       
   332     for (int i = 0; i < clients.size(); ++i) {
       
   333         XQServiceChannelPrivate *channel = clients.at(i).data();
       
   334         if (channel->object)
       
   335             ret = channel->object->receive(msg, data,sf );
       
   336     }
       
   337 
       
   338 #ifdef XQSERVICE_DEBUG
       
   339     QString s = ret.toString();
       
   340     int len=s.length();
       
   341     XQSERVICE_DEBUG_PRINT("sendLocally ret: type=%s,len=%d,value(max.1024)=%s",
       
   342                           ret.typeName(),len,qPrintable(s.left(1024)));
       
   343 #endif
       
   344     return ret ;
       
   345 }
       
   346 
       
   347 int XQServiceChannel::latestError()
       
   348 {
       
   349     XQSERVICE_DEBUG_PRINT("XQServiceChannel::latestError");
       
   350     return XQService::serviceThreadData()->latestError();
       
   351 }
       
   352 
       
   353 void XQServiceChannel::sendCommand(const QString& ch,const XQServiceCommand cmd)
       
   354 {
       
   355     XQSERVICE_DEBUG_PRINT("XQServiceChannel::sendCommand");
       
   356     XQSERVICE_DEBUG_PRINT("channel: %s, cmd: %d", qPrintable(ch), cmd);
       
   357     // feed local clients with received data
       
   358     XQServiceThreadData *td = XQService::serviceThreadData();
       
   359     QList<XQServiceChannelPrivatePointer> clients = td->clientMap[ch];
       
   360     for (int i = 0; i < clients.size(); ++i) {
       
   361         XQServiceChannelPrivate *channel = clients.at(i).data();
       
   362         if (channel->object)
       
   363             channel->object->commandReceive(cmd);
       
   364     }
       
   365 }
       
   366