- Main Page
- Related Pages
- Classes
- Files

- File List

async-dbus-proxy.cpp
1 /*
2 * This file is part of signon
3 *
4 * Copyright (C) 2009-2010 Nokia Corporation.
5 * Copyright (C) 2013 Canonical Ltd.
6 *
7 * Contact: Aurel Popirtac <ext-aurel.popirtac@nokia.com>
8 * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * version 2.1 as published by the Free Software Foundation.
13 *
14 * This library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA
23 */
24
25 #include "async-dbus-proxy.h"
26
27 #include <QDBusConnection>
28 #include <QDBusObjectPath>
29 #include <QDBusPendingCallWatcher>
30 #include <QDebug>
31 #include <QMetaMethod>
32 #include <QMetaType>
33
34 #include "connection-manager.h"
35 #include "dbusinterface.h"
36 #include "libsignoncommon.h"
37 #include "signond/signoncommon.h"
38
39 using namespace SignOn;
40
41 namespace SignOn {
42
43 class Connection
44 {
45 public:
46 Connection(const char *name, QObject *receiver, const char *slot):
47 m_name(name),
48 m_receiver(receiver),
49 m_slot(slot)
50 {
51 }
52 ~Connection() {}
53
54 const char *m_name;
55 QObject *m_receiver;
56 const char *m_slot;
57 };
58
59 } // namespace
60
61 PendingCall::PendingCall(const QString &method,
62 const QList<QVariant> &args,
63 QObject *parent):
64 QObject(parent),
65 m_method(method),
66 m_args(args),
67 m_watcher(0),
68 m_interfaceWasDestroyed(false)
69 {
70 }
71
72 PendingCall::~PendingCall()
73 {
74 }
75
76 bool PendingCall::cancel()
77 {
78 if (m_watcher) {
79 // Too late, can't cancel
80 return false;
81 }
82 Q_EMIT finished(0);
83 return true;
84 }
85
86 void PendingCall::doCall(QDBusAbstractInterface *interface)
87 {
88 QDBusPendingCall call =
89 interface->asyncCallWithArgumentList(m_method, m_args);
90 m_watcher = new QDBusPendingCallWatcher(call, this);
91 QObject::connect(m_watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
92 this, SLOT(onFinished(QDBusPendingCallWatcher*)));
93 /* Check if the interface gets destroyed while our call executes */
94 m_interfaceWasDestroyed = false;
95 QObject::connect(interface, SIGNAL(destroyed()),
96 this, SLOT(onInterfaceDestroyed()));
97 }
98
99 void PendingCall::fail(const QDBusError &err)
100 {
101 Q_EMIT error(err);
102 Q_EMIT finished(0);
103 }
104
105 void PendingCall::onFinished(QDBusPendingCallWatcher *watcher)
106 {
107 /* Check if the call failed because the interface became invalid; if
108 * so, emit a signal to instruct the AsyncDBusProxy to re-queue this
109 * operation. */
110 if (m_interfaceWasDestroyed && watcher->isError()) {
111 QDBusError::ErrorType type = watcher->error().type();
112 if (type == QDBusError::Disconnected ||
113 type == QDBusError::UnknownObject) {
114 TRACE() << "emitting retry signal";
115 Q_EMIT requeueRequested();
116 return;
117 }
118 }
119
120 if (watcher->isError()) {
121 Q_EMIT error(watcher->error());
122 } else {
123 Q_EMIT success(watcher);
124 }
125 Q_EMIT finished(watcher);
126 }
127
128 void PendingCall::onInterfaceDestroyed()
129 {
130 /* If the interface is destroyed during the lifetime of the call, this can
131 * be because the remote object got destroyed or the D-Bus connection
132 * dropped. In either case, we might have to re-queue our method call.
133 *
134 * This is done in the onFinished() slot; here we just record the event.
135 */
136 m_interfaceWasDestroyed = true;
137 }
138
139 AsyncDBusProxy::AsyncDBusProxy(const QString &service,
140 const char *interface,
141 QObject *clientObject):
142 m_serviceName(service),
143 m_interfaceName(interface),
144 m_connection(NULL),
145 m_clientObject(clientObject),
146 m_interface(NULL),
147 m_status(Incomplete)
148 {
149 }
150
151 AsyncDBusProxy::~AsyncDBusProxy()
152 {
153 qDeleteAll(m_connectionsQueue);
154 m_connectionsQueue.clear();
155
156 delete m_connection;
157 }
158
159 void AsyncDBusProxy::setStatus(Status status)
160 {
161 m_status = status;
162
163 if (status == Ready) {
164 /* connect the signals and execute all pending methods */
165 Q_FOREACH(Connection *connection, m_connectionsQueue) {
166 m_interface->connect(connection->m_name,
167 connection->m_receiver,
168 connection->m_slot);
169 }
170
171 Q_FOREACH(PendingCall *call, m_operationsQueue) {
172 call->doCall(m_interface);
173 }
174 m_operationsQueue.clear();
175 } else if (status == Invalid) {
176 /* signal error on all operations */
177 Q_FOREACH(PendingCall *call, m_operationsQueue) {
178 call->fail(m_lastError);
179 }
180 m_operationsQueue.clear();
181 }
182 }
183
184 void AsyncDBusProxy::update()
185 {
186 if (m_interface != NULL) {
187 delete m_interface;
188 m_interface = 0;
189 }
190
191 if (m_connection == NULL || m_path.isEmpty()) {
192 setStatus(Incomplete);
193 return;
194 }
195
196 if (!m_connection->isConnected()) {
197 setError(m_connection->lastError());
198 return;
199 }
200
201 m_interface = new DBusInterface(m_serviceName,
202 m_path,
203 m_interfaceName,
204 *m_connection,
205 this);
206 setStatus(Ready);
207 }
208
209 void AsyncDBusProxy::setConnection(const QDBusConnection &connection)
210 {
211 delete m_connection;
212 m_connection = new QDBusConnection(connection);
213 update();
214 }
215
216 void AsyncDBusProxy::setDisconnected()
217 {
218 TRACE();
219 delete m_connection;
220 m_connection = 0;
221 /* The daemon is dead, so certainly the object paths are also invalid */
222 m_path = QString();
223 update();
224 }
225
226 void AsyncDBusProxy::setObjectPath(const QDBusObjectPath &objectPath)
227 {
228 Q_ASSERT(m_path.isEmpty() || objectPath.path().isEmpty());
229 m_path = objectPath.path();
230 update();
231 }
232
233 void AsyncDBusProxy::setError(const QDBusError &error)
234 {
235 TRACE() << error;
236 m_lastError = error;
237 setStatus(Invalid);
238 }
239
240 PendingCall *AsyncDBusProxy::queueCall(const QString &method,
241 const QList<QVariant> &args,
242 const char *replySlot,
243 const char *errorSlot)
244 {
245 return queueCall(method, args, m_clientObject, replySlot, errorSlot);
246 }
247
248 PendingCall *AsyncDBusProxy::queueCall(const QString &method,
249 const QList<QVariant> &args,
250 QObject *receiver,
251 const char *replySlot,
252 const char *errorSlot)
253 {
254 PendingCall *call = new PendingCall(method, args, this);
255 QObject::connect(call, SIGNAL(finished(QDBusPendingCallWatcher*)),
256 this, SLOT(onCallFinished(QDBusPendingCallWatcher*)));
257 QObject::connect(call, SIGNAL(requeueRequested()),
258 this, SLOT(onRequeueRequested()));
259
260 if (errorSlot) {
261 QObject::connect(call, SIGNAL(error(const QDBusError&)),
262 receiver, errorSlot);
263 if (replySlot) {
264 QObject::connect(call, SIGNAL(success(QDBusPendingCallWatcher*)),
265 receiver, replySlot);
266 }
267 } else if (replySlot) {
268 QObject::connect(call, SIGNAL(finished(QDBusPendingCallWatcher*)),
269 receiver, replySlot);
270 }
271
272 if (m_status == Ready) {
273 call->doCall(m_interface);
274 } else if (m_status == Incomplete) {
275 enqueue(call);
276 } else {
277 QMetaObject::invokeMethod(call, "fail",
278 Qt::QueuedConnection,
279 Q_ARG(QDBusError, m_lastError));
280 }
281 return call;
282 }
283
284 bool AsyncDBusProxy::connect(const char *name,
285 QObject *receiver,
286 const char *slot)
287 {
288 /* Remember all the connections anyway, because we'll re-play them if we
289 * disconnect and reconnect again */
290 Connection *connection = new Connection(name, receiver, slot);
291 m_connectionsQueue.enqueue(connection);
292
293 if (m_status == Ready) {
294 return m_interface->connect(name, receiver, slot);
295 }
296 return true;
297 }
298
299 void AsyncDBusProxy::enqueue(PendingCall *call)
300 {
301 m_operationsQueue.enqueue(call);
302 if (!m_connection) {
303 Q_EMIT connectionNeeded();
304 }
305 if (m_path.isEmpty()) {
306 Q_EMIT objectPathNeeded();
307 }
308 }
309
310 void AsyncDBusProxy::onCallFinished(QDBusPendingCallWatcher *watcher)
311 {
312 Q_UNUSED(watcher);
313 PendingCall *call = qobject_cast<PendingCall*>(sender());
314 m_operationsQueue.removeOne(call);
315 call->deleteLater();
316 }
317
318 void AsyncDBusProxy::onRequeueRequested()
319 {
320 PendingCall *call = qobject_cast<PendingCall*>(sender());
321 enqueue(call);
322 }
323
324 SignondAsyncDBusProxy::SignondAsyncDBusProxy(const char *interface,
325 QObject *clientObject):
326 AsyncDBusProxy(SIGNOND_SERVICE, interface, clientObject)
327 {
328 setupConnection();
329 }
330
331 SignondAsyncDBusProxy::~SignondAsyncDBusProxy()
332 {
333 }
334
335 void SignondAsyncDBusProxy::setupConnection()
336 {
337 ConnectionManager *connManager = ConnectionManager::instance();
338 QObject::connect(connManager, SIGNAL(connected(const QDBusConnection&)),
339 this, SLOT(setConnection(const QDBusConnection&)));
340 QObject::connect(connManager, SIGNAL(disconnected()),
341 this, SLOT(setDisconnected()));
342 QObject::connect(this, SIGNAL(connectionNeeded()),
343 connManager, SLOT(connect()));
344 if (connManager->hasConnection()) {
345 setConnection(connManager->connection());
346 }
347 }
SignOn::DBusInterface
Definition: dbusinterface.h:33
SignOn::ConnectionManager
Definition: connection-manager.h:33