summaryrefslogtreecommitdiffstats
path: root/tqdbusobject.h
blob: 2e07a44430aa1cdc50ce0ae9f531a112e494eaba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
/* qdbusobject.h DBUS service object interface
 *
 * Copyright (C) 2005-2007 Kevin Krammer <kevin.krammer@gmx.at>
 *
 * Licensed under the Academic Free License version 2.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 * USA.
 *
 */

#ifndef TQDBUSOBJECT_H
#define TQDBUSOBJECT_H

/**
 * @page dbusservice Providing services over D-Bus
 *
 * Contents:
 * - @ref dbusservice-introduction
 * - @ref dbusservice-example
 * - @ref dbusservice-requestname
 * - @ref dbusservice-registerobjects
 * - @ref dbusservice-interfaces
 *
 * @section dbusservice-introduction Introduction
 *
 * The TQt3 bindings do not support autogeneration of service objects yet. In
 * order to provide interfaces over D-Bus, an application has to implement the
 * TQT_DBusObjectBase interface and register an instance of the resulting class
 * with the TQT_DBusConnection.
 *
 * @section dbusservice-example A simple D-Bus client example
 *
 * @code
 *   #include <dbus/tqdbusconnection.h>;
 *   #include <dbus/tqdbusobject.h>;
 *
 *   class TQStringList;
 *
 *   class TestService : public TQT_DBusObjectBase
 *   {
 *   public:
 *       TestService(const TQT_DBusConnection& connection);
 *       virtual ~TestService();
 *
 *   protected:
 *       virtual bool handleMethodCall(const TQT_DBusMessage& message);
 *
 *   private:
 *       TQT_DBusConnection m_connection;
 *
 *   private:
 *       TQStringList sortStrings(const TQStringList& list);
 *   };
 * @endcode
 * @code
 *
 *   #include <tqstringlist.h>;
 *
 *   #include <dbus/tqdbuserror.h>;
 *   #include <dbus/tqdbusmessage.h>;
 *
 *   TestService::TestService(const TQT_DBusConnection& connection) : m_connection(connection)
 *   {
 *       m_connection.registerObject("/ListSorter", this);
 *   }
 *
 *   TestService::~TestService()
 *   {
 *       m_connection.unregisterObject("/ListSorter");
 *   }
 *
 *   // return false to let D-Bus send a standard error message that the method is unknown
 *
 *   bool TestService::handleMethod(const TQT_DBusMessage& message)
 *   {
 *       if (message.interface() != "org.example.Sort") return false;
 *
 *       if (message.member() == "Strings")
 *       {
 *           // check parameters
 *
 *           if (message.count() != 1 || message[0].type() != TQT_DBusData::List)
 *           {
 *               // method signature not what we expected
 *
 *               TQT_DBusError error = TQT_DBusError::stdInvalidArgs(
 *                                "Expected one argument of type array of string");
 *
 *               TQT_DBusMessage reply = TQT_DBusMessage::methodError(message, error);
 *
 *               // send error
 *
 *               m_connection.send(reply);
 *
 *               // tell D-Bus we did handle the call
 *
 *               return true;
 *           }
 *
 *           // call implementation
 *
 *           TQStringList result = sortStrings(message[0].toTQStringList());
 *
 *           // prepare reply
 *
 *           TQT_DBusMessage reply = TQT_DBusMessage::methodReply(message);
 *
 *           reply << TQT_DBusData::fromList(result);
 *
 *           // send reply
 *
 *           m_connection.send(reply);
 *
 *           // tell D-Bus we did handle the call
 *
 *           return true;
 *       }
 *
 *       return false;
 *   }
 *
 *   TQStringList TestService::sortStrings(const TQStringList& list)
 *   {
 *       TQStringList result = list;
 *
 *       result.sort();
 *
 *       return result;
 *   }
 * @endcode
 * @code
 *   int main(int argc, char** argv)
 *   {
 *       TQApplication app(argc, argv, false);
 *
 *       TQT_DBusConnection connection = TQT_DBusConnection::sessionBus();
 *       if (!connection.isConnected())
 *           qFatal("Cannot connect to session bus");
 *
 *       // try to get a specific service name
 *       if (!connection.requestName("org.example.SortService"))
 *       {
 *           qWarning("Requesting name 'org.example.SortService' failed. "
 *                    "Will only be addressable through unique name '%s'",
 *                    connection.uniqueName().local8Bit().data());
 *       }
 *       else
 *       {
 *           qDebug("Requesting name 'org.example.SortService' successfull");
 *       }
 *
 *       TestService service(connection);
 *
 *       return app.exec();
 *    }
 * @endcode
 *
 * @section dbusservice-requestname Requesting service name
 *
 * When an application connects to D-Bus it gets a unique name generated by
 * the bus daemon.
 *
 * However, an application providing service will often want to be reachable
 * under a fixed name, like a webserver being reachable through a domain name
 * independent from its actual IP address.
 * See section @ref dbusconventions-servicename for details on service names.
 *
 * In order to get such a specific name an application has to request it
 * using TQT_DBusConnection::requestName()
 *
 * The example above request @c "org.example.SortService" but continues with
 * the default unique name in the case some other application is currently
 * owning that name.
 *
 * @section dbusservice-registerobjects Registering objects
 *
 * To make service objects available to other applications on the same
 * bus the application has to register the objects instances with the
 * connection to the bus using TQT_DBusConnection::registerObject()
 *
 * Registering means to specify an object path where the object will be
 * located, i.e. how it can be unambiguously be addressed in method calls.
 * See section @ref dbusconventions-objectpath for details on object paths.
 *
 * If the applications has introspectable objects it is recommended to
 * register an introspectable root object, i.e. using @c "/" as the path, so
 * other applications have a common place to start asking for introspection
 * data.
 *
 * In the example above a service object providing sorting services on lists is
 * registered on the path @c "/ListSorter"
 *
 * @section dbusservice-interfaces Service interfaces
 *
 * D-Bus methods and signals of a service object a grouped into interfaces.
 *
 * See section @ref dbusconventions-interfacename for details on interface
 * naming.
 *
 * An object can implement any number of interfaces, for example the interface
 * for the functionality it wants to provide and a D-Bus standard interface like
 * @c "org.freedesktop.DBus.Introspectable" for providing an XML description of
 * all its interfaces.
 *
 *
 * The service object of the example above implements just one interface
 * @c "org.example.Sort" and its handleMethodCall() explicitly checks all
 * received messages and rejects any messsage not sent to this particular
 * interface by returning @c false and thus telling the D-Bus layer to
 * generate a standard error response.
 *
 * Multiple interfaces can of course be directly implemented in one C++ class,
 * however it might sometimes be wise to delegate calls for different
 * interfaces to different implementations:
 * @code
 *   class Interface1 : public TQT_DBusObjectBase
 *   {
 *   public:
 *       Interface1(const TQT_DBusConnection&);
 *
 *   protected:
 *       virtual bool handleMethodCall(const TQT_DBusMessage&);
 *   };
 *
 *   class Interface2 : public TQT_DBusObjectBase
 *   {
 *   public:
 *       Interface2(const TQT_DBusConnection&);
 *
 *   protected:
 *       virtual bool handleMethodCall(const TQT_DBusMessage&);
 *   };
 *
 *   class MultiInterfaceService : public TQT_DBusObjectBase
 *   {
 *   public:
 *       MultiInterfaceService(const TQT_DBusConnection&);
 *
 *   protected:
 *       virtual bool handleMethodCall(const TQT_DBusMessage&);
 *
 *   private:
 *       TQMap<TQString, TQT_DBusObjectBase*> m_interfaces;
 *   };
 *
 *   MultiInterfaceService::MultiInterfaceService(const TQT_DBusConnection& connection)
 *   {
 *       m_interfaces.insert("org.example.Interface1", new Interface1(connection));
 *       m_interfaces.insert("org.example.Interface2", new Interface2(connection));
 *   }
 *
 *   bool MultiInterfaceService::handleMethodCall(const TQT_DBusMessage& message)
 *   {
 *       // delegate call to its interface handler
 *       TQT_DBusObjectBase* handler = m_interfaces[message.interface()];
 *       if (handler != 0)
 *           return delegateMethodCall->(message, handler);
 *       else
 *           return false; // no such interface
 *   }
 * @endcode
 */

/**
 * @include example-service.h
 * @example example-service.cpp
 */

#include "tqdbusmacros.h"

class TQT_DBusMessage;

/**
 * @brief Base interface for D-Bus service objects
 *
 * In order to register a service object with the TQT_DBusConnection it needs to
 * implement the interface specified by this class.
 *
 * The connection will forward all method calls that have a path equivalent
 * to the path the service object was registered with to the object's
 * handleMethodCall() method. See TQT_DBusConnection::registerObject()
 *
 * If for some reason, e.g. the call is not meant for this interface, or the
 * method is unknown, the implementation can just return @c false and the
 * connection will handle the rest.
 *
 * See section @ref dbusservice for documentation on how to use TQT_DBusObjectBase
 */
class TQDBUS_EXPORT TQT_DBusObjectBase
{
    friend class TQT_DBusConnectionPrivate;
public:
    /**
     * @brief Destroys the object
     */
    virtual ~TQT_DBusObjectBase() {}

protected:
    /**
     * @brief Method call entry point
     *
     * This method has to be implemented to handle method calls sent to the
     * service object.
     * An object implementation can handle all its interfaces in one class or
     * again forward the method call to interface implementators.
     *
     * If for some reason, e.g. the call is not meant for this interface, or
     * the method is unknown, the implementation can just return @c false and
     * the connection will handle the rest.
     *
     * If an error occurs during the method call, e.g. the number of parameters
     * or their types are not what would be expected, the service object
     * should reply with a TQT_DBusMessage of type TQT_DBusMessage::ErrorMessage
     * which in turn should include the D-Bus error describing the problem.
     * See TQT_DBusConnection::send() for sending reply messages.
     *
     * See TQT_DBusMessage::methodError() and TQT_DBusMessage::methodReply() on
     * how to create suitable reply messages for the given method call.
     *
     * @param message the method call to handle
     *
     * @return @c true if the message can be handled independent if handling
     *         resulted in an error. In this case implementations should an
     *         error reply. Returns @c false only if interface or method are
     *         unknown
     */
    virtual bool handleMethodCall(const TQT_DBusMessage& message) = 0;

    /**
     * @brief Delegate a method call to another object
     *
     * When a service object is built as a collection of separated interface
     * class instances, i.e. each interface of the object is implemented in
     * its own TQT_DBusObjectBase subclass and the main object just wanst to pass
     * on the method calls to the respective interface implementations, it
     * can do so by calling this base class method.
     *
     * Since it is a method of the base class, it can call the otherwise
     * protected handleMethodCall() of the interface implementor.
     *
     * See @ref dbusservice-interfaces for an example.
     *
     * @param message the method call to delegate
     * @param delegate the object which should handle the call instead
     *
     * @return @c true if the message can be handled independent if handling
     *         resulted in an error. In this case implementations should an
     *         error reply. Returns @c false only if interface or method are
     *         unknown
     *
     */
    bool delegateMethodCall(const TQT_DBusMessage& message, TQT_DBusObjectBase* delegate)
    {
        return delegate->handleMethodCall(message);
    }
};

#endif