source: trunk/src/dbus/qdbuspendingcall.cpp@ 122

Last change on this file since 122 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 15.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtDBus module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
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
51QT_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
126void QDBusPendingCallWatcherHelper::add(QDBusPendingCallWatcher *watcher)
127{
128 connect(this, SIGNAL(finished()), watcher, SLOT(_q_finished()), Qt::QueuedConnection);
129}
130
131QDBusPendingCallPrivate::~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
140bool 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
187void 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
209void 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
231void 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*/
243QDBusPendingCall::QDBusPendingCall(const QDBusPendingCall &other)
244 : d(other.d)
245{
246}
247
248/*!
249 \internal
250*/
251QDBusPendingCall::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*/
262QDBusPendingCall::~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*/
278QDBusPendingCall &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
311bool QDBusPendingCall::isFinished() const
312{
313 return d && (d->replyMessage.type() != QDBusMessage::InvalidMessage);
314}
315
316void 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*/
330bool 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*/
344bool 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*/
357QDBusError 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*/
379QDBusMessage 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*/
403bool 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
413class QDBusPendingCallWatcherPrivate: public QObjectPrivate
414{
415public:
416 void _q_finished();
417
418 Q_DECLARE_PUBLIC(QDBusPendingCallWatcher)
419};
420
421inline void QDBusPendingCallWatcherPrivate::_q_finished()
422{
423 Q_Q(QDBusPendingCallWatcher);
424 emit q->finished(q);
425}
426
427/*!
428 Creates a QDBusPendingCallWatcher object to watch for replies on the
429 asynchronous pending call \a call and sets this object's parent
430 to \a parent.
431*/
432QDBusPendingCallWatcher::QDBusPendingCallWatcher(const QDBusPendingCall &call, QObject *parent)
433 : QObject(*new QDBusPendingCallWatcherPrivate, parent), QDBusPendingCall(call)
434{
435 if (d) {
436 if (!d->watcherHelper)
437 d->watcherHelper = new QDBusPendingCallWatcherHelper;
438 d->watcherHelper->add(this);
439 }
440}
441
442/*!
443 Destroys this object. If this QDBusPendingCallWatcher object was the
444 last reference to the unfinished pending call, the call will be
445 canceled.
446*/
447QDBusPendingCallWatcher::~QDBusPendingCallWatcher()
448{
449}
450
451/*!
452 \fn void QDBusPendingCallWatcher::waitForFinished()
453
454 Suspends the execution of the calling thread until the reply is
455 received and processed. After this function returns, isFinished()
456 should return true, indicating the reply's contents are ready to
457 be processed.
458
459 \sa QDBusPendingReply::waitForFinished()
460*/
461void QDBusPendingCallWatcher::waitForFinished()
462{
463 if (d) {
464 d->waitForFinished();
465
466 // our signals were queued, so deliver them
467 QCoreApplication::sendPostedEvents(this, QEvent::MetaCall);
468 }
469}
470QT_END_NAMESPACE
471
472#include "moc_qdbuspendingcall.cpp"
Note: See TracBrowser for help on using the repository browser.