src/dbus/qdbuspendingcall.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtDBus module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qdbuspendingcall.h"
       
    43 #include "qdbuspendingcall_p.h"
       
    44 
       
    45 #include "qdbusconnection_p.h"
       
    46 #include "qdbusmetatype_p.h"
       
    47 #include "qcoreapplication.h"
       
    48 #include "qcoreevent.h"
       
    49 #include <private/qobject_p.h>
       
    50 
       
    51 QT_BEGIN_NAMESPACE
       
    52 
       
    53 /*!
       
    54     \class QDBusPendingCall
       
    55     \inmodule QtDBus
       
    56     \since 4.5
       
    57 
       
    58     \brief The QDBusPendingCall class refers to one pending asynchronous call
       
    59 
       
    60     A QDBusPendingCall object is a reference to a method call that was
       
    61     sent over D-Bus without waiting for a reply. QDBusPendingCall is an
       
    62     opaque type, meant to be used as a handle for a pending reply.
       
    63 
       
    64     In most programs, the QDBusPendingCall class will not be used
       
    65     directly. It can be safely replaced with the template-based
       
    66     QDBusPendingReply, in order to access the contents of the reply or
       
    67     wait for it to be complete.
       
    68 
       
    69     The QDBusPendingCallWatcher class allows one to connect to a signal
       
    70     that will indicate when the reply has arrived or if the call has
       
    71     timed out. It also provides the
       
    72     QDBusPendingCallWatcher::waitForFinished() method which will suspend
       
    73     the execution of the program until the reply has arrived.
       
    74 
       
    75     \note If you create a copy of a QDBusPendingCall object, all
       
    76           information will be shared among the many copies. Therefore,
       
    77           QDBusPendingCall is an explicitly-shared object and does not
       
    78           provide a method of detaching the copies (since they refer
       
    79           to the same pending call)
       
    80 
       
    81     \sa QDBusPendingReply, QDBusPendingCallWatcher,
       
    82         QDBusAbstractInterface::asyncCall()
       
    83 */
       
    84 
       
    85 /*!
       
    86     \class QDBusPendingCallWatcher
       
    87     \inmodule QtDBus
       
    88     \since 4.5
       
    89 
       
    90     \brief The QDBusPendingCallWatcher class provides a convenient way for
       
    91     waiting for asynchronous replies
       
    92 
       
    93     The QDBusPendingCallWatcher provides the finished() signal that will be
       
    94     emitted when a reply arrives.
       
    95 
       
    96     It is usually used like the following example:
       
    97 
       
    98     \snippet doc/src/snippets/code/src.qdbus.qdbuspendingcall.cpp 0
       
    99 
       
   100     Note that it is not necessary to keep the original QDBusPendingCall
       
   101     object around since QDBusPendingCallWatcher inherits from that class
       
   102     too.
       
   103 
       
   104     The slot connected to by the above code could be something similar
       
   105     to the following:
       
   106 
       
   107     \snippet doc/src/snippets/code/src.qdbus.qdbuspendingcall.cpp 1
       
   108 
       
   109     Note the use of QDBusPendingReply to validate the argument types in
       
   110     the reply. If the reply did not contain exactly two arguments
       
   111     (one string and one QByteArray), QDBusPendingReply::isError() will
       
   112     return true.
       
   113 
       
   114     \sa QDBusPendingReply, QDBusAbstractInterface::asyncCall()
       
   115 */
       
   116 
       
   117 /*!
       
   118     \fn void QDBusPendingCallWatcher::finished(QDBusPendingCallWatcher *self)
       
   119 
       
   120     This signal is emitted when the pending call has finished and its
       
   121     reply is available. The \a self parameter is a pointer to the
       
   122     object itself, passed for convenience so that the slot can access
       
   123     the properties and determine the contents of the reply.
       
   124 */
       
   125 
       
   126 void QDBusPendingCallWatcherHelper::add(QDBusPendingCallWatcher *watcher)
       
   127 {
       
   128     connect(this, SIGNAL(finished()), watcher, SLOT(_q_finished()), Qt::QueuedConnection);
       
   129 }
       
   130 
       
   131 QDBusPendingCallPrivate::~QDBusPendingCallPrivate()
       
   132 {
       
   133     if (pending) {
       
   134         q_dbus_pending_call_cancel(pending);
       
   135         q_dbus_pending_call_unref(pending);
       
   136     }
       
   137     delete watcherHelper;
       
   138 }
       
   139 
       
   140 bool QDBusPendingCallPrivate::setReplyCallback(QObject *target, const char *member)
       
   141 {
       
   142     receiver = target;
       
   143     metaTypes.clear();
       
   144     methodIdx = -1;
       
   145     if (!target)
       
   146         return true;;           // unsetting
       
   147 
       
   148     if (!member || !*member) {
       
   149         // would not be able to deliver a reply
       
   150         qWarning("QDBusPendingCall::setReplyCallback: error: cannot deliver a reply to %s::%s (%s)",
       
   151                  target ? target->metaObject()->className() : "(null)",
       
   152                  member ? member + 1 : "(null)",
       
   153                  target ? qPrintable(target->objectName()) : "no name");
       
   154         return false;
       
   155     }
       
   156 
       
   157     methodIdx = QDBusConnectionPrivate::findSlot(target, member + 1, metaTypes);
       
   158     if (methodIdx == -1) {
       
   159         QByteArray normalizedName = QMetaObject::normalizedSignature(member + 1);
       
   160         methodIdx = QDBusConnectionPrivate::findSlot(target, normalizedName, metaTypes);
       
   161     }
       
   162     if (methodIdx == -1) {
       
   163         // would not be able to deliver a reply
       
   164         qWarning("QDBusPendingCall::setReplyCallback: error: cannot deliver a reply to %s::%s (%s)",
       
   165                  target->metaObject()->className(),
       
   166                  member + 1, qPrintable(target->objectName()));
       
   167         return false;
       
   168     }
       
   169 
       
   170     // success
       
   171     // construct the expected signature
       
   172     int count = metaTypes.count() - 1;
       
   173     if (count == 1 && metaTypes.at(1) == QDBusMetaTypeId::message) {
       
   174         // wildcard slot, can receive anything, so don't set the signature
       
   175         return true;
       
   176     }
       
   177 
       
   178     if (metaTypes.at(count) == QDBusMetaTypeId::message)
       
   179         --count;
       
   180 
       
   181     // QList<int> is actually a vector
       
   182     // kids, don't try this at home
       
   183     setMetaTypes(count, count ? &metaTypes.at(1) : 0);
       
   184     return true;
       
   185 }
       
   186 
       
   187 void QDBusPendingCallPrivate::setMetaTypes(int count, const int *types)
       
   188 {
       
   189     expectedReplyCount = count;
       
   190     if (count == 0) {
       
   191         expectedReplySignature = QLatin1String(""); // not null
       
   192         return;
       
   193     }
       
   194 
       
   195     QByteArray sig;
       
   196     sig.reserve(count + count / 2);
       
   197     for (int i = 0; i < count; ++i) {
       
   198         const char *typeSig = QDBusMetaType::typeToSignature(types[i]);
       
   199         if (!typeSig) {
       
   200             qFatal("QDBusPendingReply: type %s is not registered with QtDBus",
       
   201                    QMetaType::typeName(types[i]));
       
   202         }
       
   203         sig += typeSig;
       
   204     }
       
   205 
       
   206     expectedReplySignature = QString::fromLatin1(sig);
       
   207 }
       
   208 
       
   209 void QDBusPendingCallPrivate::checkReceivedSignature()
       
   210 {
       
   211     if (replyMessage.type() == QDBusMessage::InvalidMessage)
       
   212         return;                 // not yet finished - no message to
       
   213                                 // validate against
       
   214     if (replyMessage.type() == QDBusMessage::ErrorMessage)
       
   215         return;                 // we don't have to check the signature of an error reply
       
   216 
       
   217     if (expectedReplySignature.isNull())
       
   218         return;                 // no signature to validate against
       
   219 
       
   220     // can't use startsWith here because a null string doesn't start or end with an empty string
       
   221     if (!replyMessage.signature().indexOf(expectedReplySignature) == 0) {
       
   222         QString errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", "
       
   223                                          "expected \"%2\"");
       
   224         replyMessage = QDBusMessage::createError(
       
   225             QDBusError::InvalidSignature,
       
   226             errorMsg.arg(replyMessage.signature(), expectedReplySignature));
       
   227 
       
   228     }
       
   229 }
       
   230 
       
   231 void QDBusPendingCallPrivate::waitForFinished()
       
   232 {
       
   233     if (replyMessage.type() != QDBusMessage::InvalidMessage)
       
   234         return;                 // already finished
       
   235 
       
   236     connection->waitForFinished(this);
       
   237 }
       
   238 
       
   239 /*!
       
   240     Creates a copy of the \a other pending asynchronous call. Note
       
   241     that both objects will refer to the same pending call.
       
   242 */
       
   243 QDBusPendingCall::QDBusPendingCall(const QDBusPendingCall &other)
       
   244     : d(other.d)
       
   245 {
       
   246 }
       
   247 
       
   248 /*!
       
   249     \internal
       
   250 */
       
   251 QDBusPendingCall::QDBusPendingCall(QDBusPendingCallPrivate *dd)
       
   252     : d(dd)
       
   253 {
       
   254 }
       
   255 
       
   256 /*!
       
   257     Destroys this copy of the QDBusPendingCall object. If this copy is
       
   258     also the last copy of a pending asynchronous call, the call will
       
   259     be canceled and no further notifications will be received. There
       
   260     will be no way of accessing the reply's contents when it arrives.
       
   261 */
       
   262 QDBusPendingCall::~QDBusPendingCall()
       
   263 {
       
   264     // d deleted by QExplicitlySharedDataPointer
       
   265 }
       
   266 
       
   267 
       
   268 /*!
       
   269     Creates a copy of the \a other pending asynchronous call and drops
       
   270     the reference to the previously-referenced call. Note that both
       
   271     objects will refer to the same pending call after this function.
       
   272 
       
   273     If this object contained the last reference of a pending
       
   274     asynchronous call, the call will be canceled and no further
       
   275     notifications will be received. There will be no way of accessing
       
   276     the reply's contents when it arrives.
       
   277 */
       
   278 QDBusPendingCall &QDBusPendingCall::operator=(const QDBusPendingCall &other)
       
   279 {
       
   280     d = other.d;
       
   281     return *this;
       
   282 }
       
   283 
       
   284 /*!
       
   285     \fn bool QDBusPendingCallWatcher::isFinished() const
       
   286 
       
   287     Returns true if the pending call has finished processing and the
       
   288     reply has been received.
       
   289 
       
   290     Note that this function only changes state if you call
       
   291     waitForFinished() or if an external D-Bus event happens, which in
       
   292     general only happens if you return to the event loop execution.
       
   293 
       
   294     \sa QDBusPendingReply::isFinished()
       
   295 */
       
   296 /*!
       
   297     \fn bool QDBusPendingReply::isFinished() const
       
   298 
       
   299     Returns true if the pending call has finished processing and the
       
   300     reply has been received. If this function returns true, the
       
   301     isError(), error() and reply() methods should return valid
       
   302     information.
       
   303 
       
   304     Note that this function only changes state if you call
       
   305     waitForFinished() or if an external D-Bus event happens, which in
       
   306     general only happens if you return to the event loop execution.
       
   307 
       
   308     \sa QDBusPendingCallWatcher::isFinished()
       
   309 */
       
   310 
       
   311 bool QDBusPendingCall::isFinished() const
       
   312 {
       
   313     return d && (d->replyMessage.type() != QDBusMessage::InvalidMessage);
       
   314 }
       
   315 
       
   316 void QDBusPendingCall::waitForFinished()
       
   317 {
       
   318     if (d) d->waitForFinished();
       
   319 }
       
   320 
       
   321 /*!
       
   322     \fn bool QDBusPendingReply::isValid() const
       
   323 
       
   324     Returns true if the reply contains a normal reply message, false
       
   325     if it contains anything else.
       
   326 
       
   327     If the pending call has not finished processing, this function
       
   328     return false.
       
   329 */
       
   330 bool QDBusPendingCall::isValid() const
       
   331 {
       
   332     return d ? d->replyMessage.type() == QDBusMessage::ReplyMessage : false;
       
   333 }
       
   334 
       
   335 /*!
       
   336     \fn bool QDBusPendingReply::isError() const
       
   337 
       
   338     Returns true if the reply contains an error message, false if it
       
   339     contains a normal method reply.
       
   340 
       
   341     If the pending call has not finished processing, this function
       
   342     also returns true.
       
   343 */
       
   344 bool QDBusPendingCall::isError() const
       
   345 {
       
   346     return d ? d->replyMessage.type() == QDBusMessage::ErrorMessage : true;
       
   347 }
       
   348 
       
   349 /*!
       
   350     \fn QDBusError QDBusPendingReply::error() const
       
   351 
       
   352     Retrieves the error content of the reply message, if it has
       
   353     finished processing. If the reply message has not finished
       
   354     processing or if it contains a normal reply message (non-error),
       
   355     this function returns an invalid QDBusError.
       
   356 */
       
   357 QDBusError QDBusPendingCall::error() const
       
   358 {
       
   359     if (d)
       
   360         return d->replyMessage;
       
   361 
       
   362     // not connected, return an error
       
   363     QDBusError err = QDBusError(QDBusError::Disconnected,
       
   364                                 QLatin1String("Not connected to D-Bus server"));
       
   365     return err;
       
   366 }
       
   367 
       
   368 /*!
       
   369     \fn QDBusMessage QDBusPendingReply::reply() const
       
   370 
       
   371     Retrieves the reply message received for the asynchronous call
       
   372     that was sent, if it has finished processing. If the pending call
       
   373     is not finished, this function returns a QDBusMessage of type
       
   374     QDBusMessage::InvalidMessage.
       
   375 
       
   376     After it has finished processing, the message type will either be
       
   377     an error message or a normal method reply message.
       
   378 */
       
   379 QDBusMessage QDBusPendingCall::reply() const
       
   380 {
       
   381     return d ? d->replyMessage : QDBusMessage::createError(error());
       
   382 }
       
   383 
       
   384 #if 0
       
   385 /*!
       
   386     Sets the slot \a member in object \a target to be called when the
       
   387     reply arrives. The slot's parameter list must match the reply
       
   388     message's arguments for it to be called.
       
   389 
       
   390     It may, optionally, contain a QDBusMessage final parameter. If it
       
   391     is present, the parameter will contain the reply message object.
       
   392 
       
   393     The callback will not be called if the reply is an error message.
       
   394 
       
   395     This function returns true if it could set the callback, false
       
   396     otherwise. It is not a guarantee that the callback will be
       
   397     called.
       
   398 
       
   399     \warning QDBusPendingCall only supports one callback per pending
       
   400              asynchronous call, even if multiple QDBusPendingCall
       
   401              objects are referencing the same pending call.
       
   402 */
       
   403 bool QDBusPendingCall::setReplyCallback(QObject *target, const char *member)
       
   404 {
       
   405     if (!d)
       
   406         return false;
       
   407 
       
   408     return d->setReplyCallback(target, member);
       
   409 }
       
   410 #endif
       
   411 
       
   412 /*!
       
   413     \since 4.6
       
   414     Creates a QDBusPendingCall object based on the error condition
       
   415     \a error. The resulting pending call object will be in the
       
   416     "finished" state and QDBusPendingReply::isError() will return true.
       
   417 
       
   418     \sa fromCompletedCall()
       
   419 */
       
   420 QDBusPendingCall QDBusPendingCall::fromError(const QDBusError &error)
       
   421 {
       
   422     return fromCompletedCall(QDBusMessage::createError(error));
       
   423 }
       
   424 
       
   425 /*!
       
   426     \since 4.6
       
   427     Creates a QDBusPendingCall object based on the message \a msg.
       
   428     The message must be of type QDBusMessage::ErrorMessage or
       
   429     QDBusMessage::ReplyMessage (that is, a message that is typical
       
   430     of a completed call).
       
   431 
       
   432     This function is useful for code that requires simulating a pending
       
   433     call, but that has already finished.
       
   434 
       
   435     \sa fromError()
       
   436 */
       
   437 QDBusPendingCall QDBusPendingCall::fromCompletedCall(const QDBusMessage &msg)
       
   438 {
       
   439     QDBusPendingCallPrivate *d = 0;
       
   440     if (msg.type() == QDBusMessage::ErrorMessage ||
       
   441         msg.type() == QDBusMessage::ReplyMessage) {
       
   442         d = new QDBusPendingCallPrivate;
       
   443         d->replyMessage = msg;
       
   444         d->connection = 0;
       
   445     }
       
   446 
       
   447     return QDBusPendingCall(d);
       
   448 }
       
   449 
       
   450 
       
   451 class QDBusPendingCallWatcherPrivate: public QObjectPrivate
       
   452 {
       
   453 public:
       
   454     void _q_finished();
       
   455 
       
   456     Q_DECLARE_PUBLIC(QDBusPendingCallWatcher)
       
   457 };
       
   458 
       
   459 inline void QDBusPendingCallWatcherPrivate::_q_finished()
       
   460 {
       
   461     Q_Q(QDBusPendingCallWatcher);
       
   462     emit q->finished(q);
       
   463 }
       
   464 
       
   465 /*!
       
   466     Creates a QDBusPendingCallWatcher object to watch for replies on the
       
   467     asynchronous pending call \a call and sets this object's parent
       
   468     to \a parent.
       
   469 */
       
   470 QDBusPendingCallWatcher::QDBusPendingCallWatcher(const QDBusPendingCall &call, QObject *parent)
       
   471     : QObject(*new QDBusPendingCallWatcherPrivate, parent), QDBusPendingCall(call)
       
   472 {
       
   473     if (d) {                    // QDBusPendingCall::d
       
   474         if (!d->watcherHelper) {
       
   475             d->watcherHelper = new QDBusPendingCallWatcherHelper;
       
   476             if (isFinished()) {
       
   477                 // cause a signal emission anyways
       
   478                 QMetaObject::invokeMethod(d->watcherHelper, "finished", Qt::QueuedConnection);
       
   479             }
       
   480         }
       
   481         d->watcherHelper->add(this);
       
   482     }
       
   483 }
       
   484 
       
   485 /*!
       
   486     Destroys this object. If this QDBusPendingCallWatcher object was the
       
   487     last reference to the unfinished pending call, the call will be
       
   488     canceled.
       
   489 */
       
   490 QDBusPendingCallWatcher::~QDBusPendingCallWatcher()
       
   491 {
       
   492 }
       
   493 
       
   494 /*!
       
   495     \fn void QDBusPendingCallWatcher::waitForFinished()
       
   496 
       
   497     Suspends the execution of the calling thread until the reply is
       
   498     received and processed. After this function returns, isFinished()
       
   499     should return true, indicating the reply's contents are ready to
       
   500     be processed.
       
   501 
       
   502     \sa QDBusPendingReply::waitForFinished()
       
   503 */
       
   504 void QDBusPendingCallWatcher::waitForFinished()
       
   505 {
       
   506     if (d) {
       
   507         d->waitForFinished();
       
   508 
       
   509         // our signals were queued, so deliver them
       
   510         QCoreApplication::sendPostedEvents(d->watcherHelper, QEvent::MetaCall);
       
   511         QCoreApplication::sendPostedEvents(this, QEvent::MetaCall);
       
   512     }
       
   513 }
       
   514 QT_END_NAMESPACE
       
   515 
       
   516 #include "moc_qdbuspendingcall.cpp"