00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "async-dbus-proxy.h"
00025
00026 #include <QDBusConnection>
00027 #include <QDBusObjectPath>
00028 #include <QDBusPendingCallWatcher>
00029 #include <QDebug>
00030 #include <QMetaMethod>
00031 #include <QMetaType>
00032
00033 #include "connection-manager.h"
00034 #include "dbusinterface.h"
00035 #include "debug.h"
00036 #include "libsignoncommon.h"
00037 #include "signond/signoncommon.h"
00038
00039 using namespace SignOn;
00040
00041 namespace SignOn {
00042
00043 class Connection
00044 {
00045 public:
00046 Connection(const char *name, QObject *receiver, const char *slot):
00047 m_name(name),
00048 m_receiver(receiver),
00049 m_slot(slot)
00050 {
00051 }
00052 ~Connection() {}
00053
00054 const char *m_name;
00055 QObject *m_receiver;
00056 const char *m_slot;
00057 };
00058
00059 }
00060
00061 PendingCall::PendingCall(const QString &method,
00062 const QList<QVariant> &args,
00063 QObject *parent):
00064 QObject(parent),
00065 m_method(method),
00066 m_args(args),
00067 m_watcher(0),
00068 m_interfaceWasDestroyed(false)
00069 {
00070 }
00071
00072 PendingCall::~PendingCall()
00073 {
00074 }
00075
00076 bool PendingCall::cancel()
00077 {
00078 if (m_watcher) {
00079
00080 return false;
00081 }
00082 Q_EMIT finished(0);
00083 return true;
00084 }
00085
00086 void PendingCall::doCall(QDBusAbstractInterface *interface)
00087 {
00088 QDBusPendingCall call =
00089 interface->asyncCallWithArgumentList(m_method, m_args);
00090 m_watcher = new QDBusPendingCallWatcher(call, this);
00091 QObject::connect(m_watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
00092 this, SLOT(onFinished(QDBusPendingCallWatcher*)));
00093
00094 m_interfaceWasDestroyed = false;
00095 QObject::connect(interface, SIGNAL(destroyed()),
00096 this, SLOT(onInterfaceDestroyed()));
00097 }
00098
00099 void PendingCall::fail(const QDBusError &err)
00100 {
00101 Q_EMIT error(err);
00102 Q_EMIT finished(0);
00103 }
00104
00105 void PendingCall::onFinished(QDBusPendingCallWatcher *watcher)
00106 {
00107
00108
00109
00110 if (m_interfaceWasDestroyed && watcher->isError()) {
00111 QDBusError::ErrorType type = watcher->error().type();
00112 if (type == QDBusError::Disconnected ||
00113 type == QDBusError::UnknownObject) {
00114 TRACE() << "emitting retry signal";
00115 Q_EMIT requeueRequested();
00116 return;
00117 }
00118 }
00119
00120 if (watcher->isError()) {
00121 Q_EMIT error(watcher->error());
00122 } else {
00123 Q_EMIT success(watcher);
00124 }
00125 Q_EMIT finished(watcher);
00126 }
00127
00128 void PendingCall::onInterfaceDestroyed()
00129 {
00130
00131
00132
00133
00134
00135
00136 m_interfaceWasDestroyed = true;
00137 }
00138
00139 AsyncDBusProxy::AsyncDBusProxy(const QString &service,
00140 const char *interface,
00141 QObject *clientObject):
00142 m_serviceName(service),
00143 m_interfaceName(interface),
00144 m_connection(NULL),
00145 m_clientObject(clientObject),
00146 m_interface(NULL),
00147 m_status(Incomplete)
00148 {
00149 }
00150
00151 AsyncDBusProxy::~AsyncDBusProxy()
00152 {
00153 qDeleteAll(m_connectionsQueue);
00154 m_connectionsQueue.clear();
00155
00156 delete m_connection;
00157 }
00158
00159 void AsyncDBusProxy::setStatus(Status status)
00160 {
00161 m_status = status;
00162
00163 if (status == Ready) {
00164
00165 Q_FOREACH(Connection *connection, m_connectionsQueue) {
00166 m_interface->connect(connection->m_name,
00167 connection->m_receiver,
00168 connection->m_slot);
00169 }
00170
00171 Q_FOREACH(PendingCall *call, m_operationsQueue) {
00172 call->doCall(m_interface);
00173 }
00174 m_operationsQueue.clear();
00175 } else if (status == Invalid) {
00176
00177 Q_FOREACH(PendingCall *call, m_operationsQueue) {
00178 call->fail(m_lastError);
00179 }
00180 m_operationsQueue.clear();
00181 }
00182 }
00183
00184 void AsyncDBusProxy::update()
00185 {
00186 if (m_interface != NULL) {
00187 delete m_interface;
00188 m_interface = 0;
00189 }
00190
00191 if (m_connection == NULL || m_path.isEmpty()) {
00192 setStatus(Incomplete);
00193 return;
00194 }
00195
00196 if (!m_connection->isConnected()) {
00197 setError(m_connection->lastError());
00198 return;
00199 }
00200
00201 m_interface = new DBusInterface(m_serviceName,
00202 m_path,
00203 m_interfaceName,
00204 *m_connection,
00205 this);
00206 setStatus(Ready);
00207 }
00208
00209 void AsyncDBusProxy::setConnection(const QDBusConnection &connection)
00210 {
00211 delete m_connection;
00212 m_connection = new QDBusConnection(connection);
00213 update();
00214 }
00215
00216 void AsyncDBusProxy::setDisconnected()
00217 {
00218 TRACE();
00219 delete m_connection;
00220 m_connection = 0;
00221
00222 m_path = QString();
00223 update();
00224 }
00225
00226 void AsyncDBusProxy::setObjectPath(const QDBusObjectPath &objectPath)
00227 {
00228 Q_ASSERT(m_path.isEmpty() || objectPath.path().isEmpty());
00229 m_path = objectPath.path();
00230 update();
00231 }
00232
00233 void AsyncDBusProxy::setError(const QDBusError &error)
00234 {
00235 TRACE() << error;
00236 m_lastError = error;
00237 setStatus(Invalid);
00238 }
00239
00240 PendingCall *AsyncDBusProxy::queueCall(const QString &method,
00241 const QList<QVariant> &args,
00242 const char *replySlot,
00243 const char *errorSlot)
00244 {
00245 return queueCall(method, args, m_clientObject, replySlot, errorSlot);
00246 }
00247
00248 PendingCall *AsyncDBusProxy::queueCall(const QString &method,
00249 const QList<QVariant> &args,
00250 QObject *receiver,
00251 const char *replySlot,
00252 const char *errorSlot)
00253 {
00254 PendingCall *call = new PendingCall(method, args, this);
00255 QObject::connect(call, SIGNAL(finished(QDBusPendingCallWatcher*)),
00256 this, SLOT(onCallFinished(QDBusPendingCallWatcher*)));
00257 QObject::connect(call, SIGNAL(requeueRequested()),
00258 this, SLOT(onRequeueRequested()));
00259
00260 if (errorSlot) {
00261 QObject::connect(call, SIGNAL(error(const QDBusError&)),
00262 receiver, errorSlot);
00263 if (replySlot) {
00264 QObject::connect(call, SIGNAL(success(QDBusPendingCallWatcher*)),
00265 receiver, replySlot);
00266 }
00267 } else if (replySlot) {
00268 QObject::connect(call, SIGNAL(finished(QDBusPendingCallWatcher*)),
00269 receiver, replySlot);
00270 }
00271
00272 if (m_status == Ready) {
00273 call->doCall(m_interface);
00274 } else if (m_status == Incomplete) {
00275 enqueue(call);
00276 } else {
00277 QMetaObject::invokeMethod(call, "fail",
00278 Qt::QueuedConnection,
00279 Q_ARG(QDBusError, m_lastError));
00280 }
00281 return call;
00282 }
00283
00284 bool AsyncDBusProxy::connect(const char *name,
00285 QObject *receiver,
00286 const char *slot)
00287 {
00288
00289
00290 Connection *connection = new Connection(name, receiver, slot);
00291 m_connectionsQueue.enqueue(connection);
00292
00293 if (m_status == Ready) {
00294 return m_interface->connect(name, receiver, slot);
00295 }
00296 return true;
00297 }
00298
00299 void AsyncDBusProxy::enqueue(PendingCall *call)
00300 {
00301 m_operationsQueue.enqueue(call);
00302 if (!m_connection) {
00303 Q_EMIT connectionNeeded();
00304 }
00305 if (m_path.isEmpty()) {
00306 Q_EMIT objectPathNeeded();
00307 }
00308 }
00309
00310 void AsyncDBusProxy::onCallFinished(QDBusPendingCallWatcher *watcher)
00311 {
00312 Q_UNUSED(watcher);
00313 PendingCall *call = qobject_cast<PendingCall*>(sender());
00314 m_operationsQueue.removeOne(call);
00315 call->deleteLater();
00316 }
00317
00318 void AsyncDBusProxy::onRequeueRequested()
00319 {
00320 PendingCall *call = qobject_cast<PendingCall*>(sender());
00321 enqueue(call);
00322 }
00323
00324 SignondAsyncDBusProxy::SignondAsyncDBusProxy(const char *interface,
00325 QObject *clientObject):
00326 AsyncDBusProxy(SIGNOND_SERVICE, interface, clientObject)
00327 {
00328 setupConnection();
00329 }
00330
00331 SignondAsyncDBusProxy::~SignondAsyncDBusProxy()
00332 {
00333 }
00334
00335 void SignondAsyncDBusProxy::setupConnection()
00336 {
00337 ConnectionManager *connManager = ConnectionManager::instance();
00338 QObject::connect(connManager, SIGNAL(connected(const QDBusConnection&)),
00339 this, SLOT(setConnection(const QDBusConnection&)));
00340 QObject::connect(connManager, SIGNAL(disconnected()),
00341 this, SLOT(setDisconnected()));
00342 QObject::connect(this, SIGNAL(connectionNeeded()),
00343 connManager, SLOT(connect()));
00344 if (connManager->hasConnection()) {
00345 setConnection(connManager->connection());
00346 }
00347 }