1 /**
2 * This file is part of TelepathyQt
3 *
4 * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
5 * @copyright Copyright (C) 2010 Nokia Corporation
6 * @license LGPL 2.1
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include <TelepathyQt/DBusProxyFactory>
24 #include "TelepathyQt/dbus-proxy-factory-internal.h"
25
26 #include "TelepathyQt/_gen/dbus-proxy-factory.moc.hpp"
27 #include "TelepathyQt/_gen/dbus-proxy-factory-internal.moc.hpp"
28
29 #include "TelepathyQt/debug-internal.h"
30
31 #include <TelepathyQt/DBusProxy>
32 #include <TelepathyQt/ReadyObject>
33 #include <TelepathyQt/PendingReady>
34
35 #include <QDBusConnection>
36
37 namespace Tp
38 {
39
40 struct TP_QT_NO_EXPORT DBusProxyFactory::Private
41 {
PrivateTp::DBusProxyFactory::Private42 Private(const QDBusConnection &bus)
43 : bus(bus),
44 cache(new Cache)
45 {
46 }
47
~PrivateTp::DBusProxyFactory::Private48 ~Private()
49 {
50 delete cache;
51 }
52
53 QDBusConnection bus;
54 Cache *cache;
55 };
56
57 /**
58 * \class DBusProxyFactory
59 * \ingroup utils
60 * \headerfile TelepathyQt/dbus-proxy-factory.h <TelepathyQt/DBusProxyFactory>
61 *
62 * \brief The DBusProxyFactory class is a base class for all D-Bus proxy factory
63 * classes. Handles proxy caching and making them ready as appropriate.
64 */
65
66 /**
67 * Construct a new DBusProxyFactory object.
68 *
69 * The intention for storing the bus here is that it generally doesn't make sense to construct
70 * proxies for multiple buses in the same context. Allowing that would lead to more complex keying
71 * needs in the cache, as well.
72 *
73 * \param bus The D-Bus bus connection for the objects constructed using this factory.
74 */
DBusProxyFactory(const QDBusConnection & bus)75 DBusProxyFactory::DBusProxyFactory(const QDBusConnection &bus)
76 : mPriv(new Private(bus))
77 {
78 }
79
80 /**
81 * Class destructor.
82 */
~DBusProxyFactory()83 DBusProxyFactory::~DBusProxyFactory()
84 {
85 delete mPriv;
86 }
87
88 /**
89 * Return the D-Bus connection all of the proxies from this factory communicate with.
90 *
91 * \return A QDBusConnection object.
92 */
dbusConnection() const93 const QDBusConnection &DBusProxyFactory::dbusConnection() const
94 {
95 return mPriv->bus;
96 }
97
98 /**
99 * Return a cached proxy with the given \a busName and \a objectPath.
100 *
101 * If a proxy has not been previously put into the cache by nowHaveProxy for those identifying
102 * attributes, or a previously cached proxy has since been invalidated and/or destroyed, a \c Null
103 * shared pointer is returned instead.
104 *
105 * \param busName Bus name of the proxy to return.
106 * \param objectPath Object path of the proxy to return.
107 * \return A pointer to the DBusProxy object, if any.
108 */
cachedProxy(const QString & busName,const QString & objectPath) const109 DBusProxyPtr DBusProxyFactory::cachedProxy(const QString &busName,
110 const QString &objectPath) const
111 {
112 QString finalName = finalBusNameFrom(busName);
113 return mPriv->cache->get(Cache::Key(finalName, objectPath));
114 }
115
116 /**
117 * Should be called by subclasses when they have a proxy, be it a newly-constructed one or one from
118 * the cache.
119 *
120 * This function will then do the rest of the factory work, including caching the proxy if it's not
121 * cached already, doing any initialPrepare()/readyPrepare() work if appropriate, and making the
122 * features from featuresFor() ready if they aren't already.
123 *
124 * The returned PendingReady only finishes when the initialPrepare() and readyPrepare() operations
125 * for the proxy has completed, and the requested features have all been made ready (or found unable
126 * to be made ready). Note that this might have happened already before calling this function, if
127 * the proxy was not a newly created one, but was looked up from the cache. DBusProxyFactory handles
128 * the necessary subleties for this to work.
129 *
130 * Access to the proxy instance is allowed as soon as this method returns through
131 * PendingReady::proxy(), if the proxy is needed in a context where it's not required to be ready.
132 *
133 * \param proxy The proxy which the factory should now make sure is prepared and made ready.
134 * \return A PendingReady operation which will emit PendingReady::finished
135 * when the proxy is usable.
136 */
nowHaveProxy(const DBusProxyPtr & proxy) const137 PendingReady *DBusProxyFactory::nowHaveProxy(const DBusProxyPtr &proxy) const
138 {
139 Q_ASSERT(!proxy.isNull());
140
141 mPriv->cache->put(proxy);
142 return new PendingReady(SharedPtr<DBusProxyFactory>((DBusProxyFactory*) this),
143 proxy, featuresFor(proxy));
144 }
145
146 /**
147 * \fn QString DBusProxyFactory::finalBusNameFrom(const QString &uniqueOrWellKnown) const
148 *
149 * "Normalize" a bus name according to the rules for the proxy class to construct.
150 *
151 * Should be implemented by subclasses to transform the application-specified name \a
152 * uniqueOrWellKnown to whatever the proxy constructed for that name would have in its
153 * DBusProxy::busName() in the end.
154 *
155 * For StatelessDBusProxy sub-classes this should mostly be an identity transform, while for
156 * StatefulDBusProxy sub-classes StatefulDBusProxy::uniqueNameFrom() or an equivalent thereof should
157 * be used in most cases.
158 *
159 * If this is not implemented correctly, caching won't work properly.
160 *
161 * \param uniqueOrWellKnown Any valid D-Bus service name, either unique or well-known.
162 * \return Whatever that name would turn to, when a proxy is constructed for it.
163 */
164
165 /**
166 * Allows subclasses to do arbitrary manipulation on the proxy before it is attempted to be made
167 * ready.
168 *
169 * If a non-\c NULL operation is returned, the completion of that operation is waited for before
170 * starting to make the object ready whenever nowHaveProxy() is called the first time around for a
171 * given proxy.
172 *
173 * \todo FIXME actually implement this... :) Currently just a vtable placeholder.
174 * \param proxy The just-constructed proxy to be prepared.
175 * \return \c NULL ie. nothing to do.
176 */
initialPrepare(const DBusProxyPtr & proxy) const177 PendingOperation *DBusProxyFactory::initialPrepare(const DBusProxyPtr &proxy) const
178 {
179 // Nothing we could think about needs doing
180 return NULL;
181 }
182
183 /**
184 * Allows subclasses to do arbitrary manipulation on the proxy after it has been made ready.
185 *
186 * If a non-\c NULL operation is returned, the completion of that operation is waited for before
187 * signaling that the object is ready for use after ReadyObject::becomeReady() for it has finished
188 * whenever nowHaveProxy() is called the first time around for a given proxy.
189 *
190 * \todo FIXME actually implement this... :) Currently just a vtable placeholder.
191 * \param proxy The just-readified proxy to be prepared.
192 * \return \c NULL ie. nothing to do.
193 */
readyPrepare(const DBusProxyPtr & proxy) const194 PendingOperation *DBusProxyFactory::readyPrepare(const DBusProxyPtr &proxy) const
195 {
196 // Nothing we could think about needs doing
197 return NULL;
198 }
199
200 /**
201 * \fn Features DBusProxyFactory::featuresFor(const SharedPtr<RefCounted> &proxy) const
202 *
203 * Return the features which should be made ready on a given proxy.
204 *
205 * This can be used to implement instance-specific features based on arbitrary criteria.
206 * FixedFeatureFactory implements this as a fixed set of features independent of the instance,
207 * however.
208 *
209 * It should be noted that if an empty set of features is returned, ReadyObject::becomeReady() is
210 * not called at all. In other words, any "core feature" is not automatically added to the requested
211 * features. This is to enable setting a factory to not make proxies ready at all, which is useful
212 * eg. in the case of account editing UIs which aren't interested in the state of Connection objects
213 * for the Account objects they're editing.
214 *
215 * \param proxy The proxy on which the returned features will be made ready.
216 * \return A list of Feature objects.
217 */
218
Cache()219 DBusProxyFactory::Cache::Cache()
220 {
221 }
222
~Cache()223 DBusProxyFactory::Cache::~Cache()
224 {
225 }
226
get(const Key & key) const227 DBusProxyPtr DBusProxyFactory::Cache::get(const Key &key) const
228 {
229 DBusProxyPtr proxy(proxies.value(key));
230
231 if (proxy.isNull() || !proxy->isValid()) {
232 // Weak pointer invalidated or proxy invalidated during this mainloop iteration and we still
233 // haven't got the invalidated() signal for it
234 return DBusProxyPtr();
235 }
236
237 return proxy;
238 }
239
put(const DBusProxyPtr & proxy)240 void DBusProxyFactory::Cache::put(const DBusProxyPtr &proxy)
241 {
242 if (proxy->busName().isEmpty()) {
243 debug() << "Not inserting proxy" << proxy.data() << "with no bus name to factory cache";
244 return;
245 } else if (!proxy->isValid()) {
246 debug() << "Not inserting to factory cache invalid proxy - proxy is for" <<
247 proxy->busName() << ',' << proxy->objectPath();
248 return;
249 }
250
251 Key key(proxy->busName(), proxy->objectPath());
252
253 DBusProxyPtr existingProxy(proxies.value(key));
254 if (!existingProxy || existingProxy != proxy) {
255 // Disconnect the invalidated signal from the proxy we're replacing, so it won't uselessly
256 // cause the new (hopefully valid) proxy to be dropped from the cache if it arrives late.
257 //
258 // The window in which this makes a difference is very slim but existent; namely, somebody
259 // must request a proxy from the factory in the same mainloop iteration as an otherwise
260 // matching proxy has invalidated itself. The invalidation signal would be delivered and
261 // processed only during the next mainloop iteration.
262 if (existingProxy) {
263 Q_ASSERT(!existingProxy->isValid());
264 existingProxy->disconnect(
265 SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
266 this,
267 SLOT(onProxyInvalidated(Tp::DBusProxy*)));
268
269 debug() << "Replacing invalidated proxy" << existingProxy.data() << "in cache for name"
270 << existingProxy->busName() << ',' << existingProxy->objectPath();
271 }
272
273 connect(proxy.data(),
274 SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
275 SLOT(onProxyInvalidated(Tp::DBusProxy*)));
276
277 debug() << "Inserting to factory cache proxy for" << key;
278 proxies.insert(key, proxy);
279 }
280 }
281
onProxyInvalidated(Tp::DBusProxy * proxy)282 void DBusProxyFactory::Cache::onProxyInvalidated(Tp::DBusProxy *proxy)
283 {
284 Key key(proxy->busName(), proxy->objectPath());
285
286 // Not having it would indicate invalidated() signaled twice for the same proxy, or us having
287 // connected to two proxies with the same key, neither of which should happen
288 Q_ASSERT(proxies.contains(key));
289
290 debug() << "Removing from factory cache invalidated proxy for" << key;
291
292 proxies.remove(key);
293 }
294
295 }
296