~ubuntu-branches/ubuntu/quantal/zeitgeist/quantal

« back to all changes in this revision

Viewing changes to extensions/ds-registry.vala

  • Committer: Package Import Robot
  • Author(s): Didier Roche
  • Date: 2011-11-15 11:15:56 UTC
  • mfrom: (1.1.13)
  • Revision ID: package-import@ubuntu.com-20111115111556-4lmc5wdvjrsdm0ss
Tags: 0.8.99~alpha1-1ubuntu1
Upload to ubuntu the new zeitgeist rewritten in vala

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ds-registry.vala
 
2
 *
 
3
 * Copyright © 2011 Michal Hruby <michal.mhr@gmail.com>
 
4
 * Copyright © 2011 Collabora Ltd.
 
5
 *             By Siegfried-Angel Gevatter Pujals <siegfried@gevatter.com>
 
6
 *
 
7
 * Based upon a Python implementation (2009-2010) by:
 
8
 *  Siegfried-Angel Gevatter Pujals <siegfried@gevatter.com>
 
9
 *  Mikkel Kamstrup Erlandsen <mikkel.kamstrup@gmail.com>
 
10
 *
 
11
 * This program is free software: you can redistribute it and/or modify
 
12
 * it under the terms of the GNU Lesser General Public License as published by
 
13
 * the Free Software Foundation, either version 2.1 of the License, or
 
14
 * (at your option) any later version.
 
15
 *
 
16
 * This program is distributed in the hope that it will be useful,
 
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
19
 * GNU General Public License for more details.
 
20
 *
 
21
 * You should have received a copy of the GNU Lesser General Public License
 
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
23
 *
 
24
 */
 
25
 
 
26
namespace Zeitgeist
 
27
{
 
28
    [DBus (name = "org.gnome.zeitgeist.DataSourceRegistry")]
 
29
    public interface RemoteRegistry: Object
 
30
    {
 
31
        [DBus (signature = "a(sssa(asaasay)bxb)")]
 
32
        public abstract Variant get_data_sources () throws Error;
 
33
        public abstract bool register_data_source (string unique_id,
 
34
            string name, string description,
 
35
            [DBus (signature = "a(asaasay)")] Variant event_templates, BusName? sender)
 
36
            throws Error;
 
37
        public abstract void set_data_source_enabled (string unique_id,
 
38
            bool enabled) throws Error;
 
39
        [DBus (signature = "(sssa(asaasay)bxb)")]
 
40
        public abstract Variant get_data_source_from_id (string id) throws Error;
 
41
 
 
42
        public signal void data_source_disconnected (
 
43
            [DBus (signature = "(sssa(asaasay)bxb)")] Variant data_source);
 
44
        public signal void data_source_enabled (string unique_id,
 
45
            bool enabled);
 
46
        public signal void data_source_registered (
 
47
            [DBus (signature = "(sssa(asaasay)bxb)")] Variant data_source);
 
48
    }
 
49
 
 
50
    class DataSource: Object
 
51
    {
 
52
        public string unique_id { get; set; }
 
53
        public string name { get; set; }
 
54
        public string description { get; set; }
 
55
 
 
56
        public GenericArray<Event>? event_templates { get; set; }
 
57
 
 
58
        public bool enabled { get; set; }
 
59
        public bool running { get; set; }
 
60
        public int64 timestamp { get; set; }
 
61
 
 
62
        public DataSource ()
 
63
        {
 
64
            Object ();
 
65
        }
 
66
 
 
67
        public DataSource.full (string unique_id, string name,
 
68
            string description, GenericArray<Event> templates)
 
69
        {
 
70
            Object (unique_id: unique_id, name: name, description: description,
 
71
                event_templates: templates);
 
72
        }
 
73
 
 
74
        public DataSource.from_variant (Variant variant,
 
75
            bool reset_running=false)
 
76
        {
 
77
            warn_if_fail (
 
78
                variant.get_type_string () == "(sssa("+Utils.SIG_EVENT+")bxb)"
 
79
                || variant.get_type_string () == "sssa("+Utils.SIG_EVENT+")");
 
80
            var iter = variant.iterator ();
 
81
 
 
82
            assert (iter.n_children () >= 4);
 
83
            unique_id = iter.next_value ().get_string ();
 
84
            name = iter.next_value ().get_string ();
 
85
            description = iter.next_value ().get_string ();
 
86
            event_templates = Events.from_variant (iter.next_value ());
 
87
 
 
88
            if (iter.n_children () > 4)
 
89
            {
 
90
                running = iter.next_value ().get_boolean ();
 
91
                if (reset_running)
 
92
                    running = false;
 
93
                timestamp = iter.next_value ().get_int64 ();
 
94
                enabled = iter.next_value ().get_boolean ();
 
95
            }
 
96
        }
 
97
 
 
98
        public Variant to_variant ()
 
99
        {
 
100
            var vb = new VariantBuilder (new VariantType (
 
101
                "(sssa("+Utils.SIG_EVENT+")bxb)"));
 
102
 
 
103
            vb.add ("s", unique_id);
 
104
            vb.add ("s", name);
 
105
            vb.add ("s", description);
 
106
            if (event_templates != null && event_templates.length > 0)
 
107
            {
 
108
                vb.add_value (Events.to_variant (event_templates));
 
109
            }
 
110
            else
 
111
            {
 
112
                vb.open (new VariantType ("a("+Utils.SIG_EVENT+")"));
 
113
                vb.close ();
 
114
            }
 
115
 
 
116
            vb.add ("b", running);
 
117
            vb.add ("x", timestamp);
 
118
            vb.add ("b", enabled);
 
119
 
 
120
            return vb.end ();
 
121
        }
 
122
    }
 
123
 
 
124
    namespace DataSources
 
125
    {
 
126
        private const string SIG_DATASOURCES =
 
127
            "a(sssa("+Utils.SIG_EVENT+")bxb)";
 
128
 
 
129
        private static HashTable<string, DataSource> from_variant (
 
130
            Variant sources_variant, bool reset_running=false)
 
131
        {
 
132
            var registry = new HashTable<string, DataSource> (
 
133
                str_hash, str_equal);
 
134
 
 
135
            warn_if_fail (
 
136
                sources_variant.get_type_string() == SIG_DATASOURCES);
 
137
            foreach (Variant ds_variant in sources_variant)
 
138
            {
 
139
                DataSource ds = new DataSource.from_variant (ds_variant,
 
140
                    reset_running);
 
141
                registry.insert (ds.unique_id, ds);
 
142
            }
 
143
 
 
144
            return registry;
 
145
        }
 
146
 
 
147
        public static Variant to_variant (
 
148
            HashTable<string, DataSource> sources)
 
149
        {
 
150
            var vb = new VariantBuilder (new VariantType (SIG_DATASOURCES));
 
151
 
 
152
            List<unowned DataSource> data_sources = sources.get_values ();
 
153
            data_sources.sort ((a, b) =>
 
154
            {
 
155
                return strcmp (a.unique_id, b.unique_id);
 
156
            });
 
157
 
 
158
            foreach (unowned DataSource ds in data_sources)
 
159
            {
 
160
                vb.add_value (ds.to_variant ());
 
161
            }
 
162
 
 
163
            return vb.end ();
 
164
        }
 
165
    }
 
166
 
 
167
    class DataSourceRegistry: Extension, RemoteRegistry
 
168
    {
 
169
        private HashTable<string, DataSource> sources;
 
170
        private HashTable<string, GenericArray<BusName>> running;
 
171
        private uint registration_id;
 
172
        private bool dirty;
 
173
 
 
174
        private static const uint DISK_WRITE_TIMEOUT = 5 * 60; // 5 minutes
 
175
 
 
176
        DataSourceRegistry ()
 
177
        {
 
178
            Object ();
 
179
        }
 
180
 
 
181
        construct
 
182
        {
 
183
            running = new HashTable<string, GenericArray<BusName?>>(
 
184
                str_hash, str_equal);
 
185
 
 
186
            Variant? registry = retrieve_config ("registry",
 
187
                DataSources.SIG_DATASOURCES);
 
188
            if (registry != null)
 
189
                sources = DataSources.from_variant (registry, true);
 
190
            else
 
191
                sources = new HashTable<string, DataSource> (
 
192
                    str_hash, str_equal);
 
193
 
 
194
            // this will be called after bus is acquired, so it shouldn't block
 
195
            try
 
196
            {
 
197
                var connection = Bus.get_sync (BusType.SESSION, null);
 
198
                registration_id = connection.register_object<RemoteRegistry> (
 
199
                    "/org/gnome/zeitgeist/data_source_registry", this);
 
200
 
 
201
                connection.signal_subscribe ("org.freedesktop.DBus",
 
202
                    "org.freedesktop.DBus", "NameOwnerChanged",
 
203
                    "/org/freedesktop/DBus", null, 0,
 
204
                    name_owner_changed);
 
205
            }
 
206
            catch (Error err)
 
207
            {
 
208
                warning ("%s", err.message);
 
209
            }
 
210
 
 
211
            // Changes are saved to the DB every few seconds and at unload.
 
212
            Timeout.add_seconds (DISK_WRITE_TIMEOUT, flush, Priority.LOW);
 
213
        }
 
214
 
 
215
        public override void unload ()
 
216
        {
 
217
            try
 
218
            {
 
219
                var connection = Bus.get_sync (BusType.SESSION, null);
 
220
                if (registration_id != 0)
 
221
                {
 
222
                    connection.unregister_object (registration_id);
 
223
                    registration_id = 0;
 
224
                }
 
225
            }
 
226
            catch (Error err)
 
227
            {
 
228
                warning ("%s", err.message);
 
229
            }
 
230
 
 
231
            flush ();
 
232
            debug ("%s, this.ref_count = %u", Log.METHOD, this.ref_count);
 
233
        }
 
234
 
 
235
        public Variant get_data_sources ()
 
236
        {
 
237
            return DataSources.to_variant (sources);
 
238
        }
 
239
 
 
240
        private bool is_sender_known (BusName sender,
 
241
            GenericArray<BusName> sender_array)
 
242
        {
 
243
            for (int i = 0; i < sender_array.length; i++)
 
244
            {
 
245
                if (sender == sender_array[i])
 
246
                    return true;
 
247
            }
 
248
            return false;
 
249
        }
 
250
 
 
251
        public bool register_data_source (string unique_id, string name,
 
252
            string description, Variant event_templates, BusName? sender)
 
253
        {
 
254
            debug ("%s: %s, %s, %s", Log.METHOD, unique_id, name, description);
 
255
            if (sender == null)
 
256
            {
 
257
                warning ("%s: sender == null, ignoring request", Log.METHOD);
 
258
                return false;
 
259
            }
 
260
 
 
261
 
 
262
            var sender_array = running.lookup (unique_id);
 
263
            if (sender_array == null)
 
264
            {
 
265
                running.insert (unique_id, new GenericArray<BusName?>());
 
266
                running.lookup (unique_id).add (sender);
 
267
            }
 
268
            else if (is_sender_known (sender, sender_array))
 
269
            {
 
270
                running.lookup (unique_id).add (sender);
 
271
            }
 
272
 
 
273
            unowned DataSource? ds = sources.lookup (unique_id);
 
274
            if (ds != null)
 
275
            {
 
276
                var templates = Events.from_variant (event_templates);
 
277
                ds.name = name;
 
278
                ds.description = description;
 
279
                ds.event_templates = templates;
 
280
                ds.timestamp = Timestamp.now ();
 
281
                ds.running = true;
 
282
                dirty = true;
 
283
 
 
284
                data_source_registered (ds.to_variant ());
 
285
 
 
286
                return ds.enabled;
 
287
            }
 
288
            else
 
289
            {
 
290
                var templates = Events.from_variant (event_templates);
 
291
                DataSource new_ds = new DataSource.full (unique_id, name,
 
292
                    description, templates);
 
293
                new_ds.enabled = true;
 
294
                new_ds.running = true;
 
295
                new_ds.timestamp = Timestamp.now ();
 
296
                sources.insert (unique_id, new_ds);
 
297
                dirty = true;
 
298
 
 
299
                data_source_registered (new_ds.to_variant ());
 
300
 
 
301
                return new_ds.enabled;
 
302
            }
 
303
 
 
304
        }
 
305
 
 
306
        public void set_data_source_enabled (string unique_id, bool enabled)
 
307
        {
 
308
            debug ("%s: %s, %d", Log.METHOD, unique_id, (int) enabled);
 
309
            unowned DataSource? ds = sources.lookup (unique_id);
 
310
            if (ds != null)
 
311
            {
 
312
                if (ds.enabled != enabled)
 
313
                {
 
314
                    ds.enabled = enabled;
 
315
                    dirty = true;
 
316
                    data_source_enabled (unique_id, enabled);
 
317
                }
 
318
            }
 
319
            else
 
320
            {
 
321
                warning ("DataSource \"%s\" isn't registered!", unique_id);
 
322
            }
 
323
        }
 
324
 
 
325
        public Variant get_data_source_from_id (string unique_id) throws Error
 
326
        {
 
327
            unowned DataSource? ds = sources.lookup (unique_id);
 
328
            if (ds != null)
 
329
            {
 
330
                return ds.to_variant ();
 
331
            }
 
332
 
 
333
            throw new EngineError.INVALID_KEY (
 
334
                "Datasource with unique ID: %s not found".printf (unique_id));
 
335
        }
 
336
 
 
337
        public override void pre_insert_events (GenericArray<Event?> events,
 
338
            BusName? sender)
 
339
        {
 
340
            foreach (string unique_id in running.get_keys())
 
341
            {
 
342
                GenericArray<BusName?> bus_names = running.lookup (unique_id);
 
343
                if (is_sender_known (sender, bus_names))
 
344
                {
 
345
                    var data_source = sources.lookup (unique_id);
 
346
 
 
347
                    data_source.timestamp =  Timestamp.now ();
 
348
                    dirty = true;
 
349
 
 
350
                    if (!data_source.enabled)
 
351
                    {
 
352
                        for (int i = 0; i < events.length; i++)
 
353
                            events[i] = null;
 
354
                    }
 
355
                }
 
356
            }
 
357
        }
 
358
 
 
359
        /*
 
360
         * Cleanup disconnected clients and mark data-sources as not running
 
361
         * when no client remains.
 
362
         **/
 
363
        private void name_owner_changed (DBusConnection conn, string sender,
 
364
            string path, string interface_name, string signal_name,
 
365
            Variant parameters)
 
366
        {
 
367
            var name = parameters.get_child_value (0).dup_string ();
 
368
            //var old_owner = parameters.get_child_value (1).dup_string ();
 
369
            var new_owner = parameters.get_child_value (2).dup_string ();
 
370
            if (new_owner != "") return;
 
371
 
 
372
            // Are there data-sources with this bus name?
 
373
            var disconnected_ds = new GenericArray<DataSource> ();
 
374
            {
 
375
                var iter = HashTableIter<string, GenericArray<BusName?>> (
 
376
                    running);
 
377
                unowned string uid;
 
378
                unowned GenericArray<BusName> name_arr;
 
379
                while (iter.next (out uid, out name_arr))
 
380
                {
 
381
                    for (int i = 0; i < name_arr.length; i++)
 
382
                    {
 
383
                        if (name_arr[i] == name)
 
384
                        {
 
385
                            disconnected_ds.add (sources.lookup (uid));
 
386
                            name_arr.remove_index_fast (i--);
 
387
                        }
 
388
                    }
 
389
                }
 
390
            }
 
391
 
 
392
            if (disconnected_ds.length == 0) return;
 
393
 
 
394
            for (int i = 0; i < disconnected_ds.length; i++)
 
395
            {
 
396
                var ds = disconnected_ds[i];
 
397
                unowned string uid = ds.unique_id;
 
398
                debug ("Client disconnected: %s [%s]", ds.name, uid);
 
399
 
 
400
                // FIXME: Update here or change semantics to "last insert"?
 
401
                ds.timestamp = Timestamp.now ();
 
402
                dirty = true;
 
403
 
 
404
                if (running.lookup (uid).length == 0)
 
405
                {
 
406
                    debug ("No remaining client running: %s [%s]",
 
407
                        ds.name, uid);
 
408
                    running.remove (uid);
 
409
                    ds.running = false;
 
410
 
 
411
                    data_source_disconnected (ds.to_variant ());
 
412
                }
 
413
            }
 
414
        }
 
415
 
 
416
        private bool flush ()
 
417
        {
 
418
            if (dirty)
 
419
            {
 
420
                Variant v = DataSources.to_variant (sources);
 
421
                store_config ("registry", v);
 
422
                dirty = false;
 
423
            }
 
424
            return true;
 
425
        }
 
426
    }
 
427
 
 
428
    [ModuleInit]
 
429
#if BUILTIN_EXTENSIONS
 
430
    public static Type data_source_registry_init (TypeModule module)
 
431
    {
 
432
#else
 
433
    public static Type extension_register (TypeModule module)
 
434
    {
 
435
#endif
 
436
        return typeof (DataSourceRegistry);
 
437
    }
 
438
}
 
439
 
 
440
// vim:expandtab:ts=4:sw=4