[tracker/gdbus-store: 11/13] tracker-store: Port to GDBus/Vala



commit 1ddcdd8f5c40c4d7b0f30a24c877acbf16cfb1ce
Author: Jürg Billeter <j bitron ch>
Date:   Wed Jan 12 11:48:35 2011 +0100

    tracker-store: Port to GDBus/Vala

 configure.ac                                 |    2 -
 src/tracker-store/.gitignore                 |    8 +
 src/tracker-store/Makefile.am                |   93 ++--
 src/tracker-store/tracker-backup.c           |  209 ------
 src/tracker-store/tracker-backup.h           |   64 --
 src/tracker-store/tracker-backup.vala        |   98 +++
 src/tracker-store/tracker-config.vapi        |   27 +
 src/tracker-store/tracker-dbus.c             |  382 -----------
 src/tracker-store/tracker-dbus.h             |   42 --
 src/tracker-store/tracker-dbus.vala          |  258 ++++++++
 src/tracker-store/tracker-events.vapi        |   36 +
 src/tracker-store/tracker-locale-change.c    |   16 +-
 src/tracker-store/tracker-locale-change.vapi |   25 +
 src/tracker-store/tracker-main.c             |   15 +-
 src/tracker-store/tracker-marshal.list       |   12 -
 src/tracker-store/tracker-resources.c        |  767 ----------------------
 src/tracker-store/tracker-resources.h        |   95 ---
 src/tracker-store/tracker-resources.vala     |  337 ++++++++++
 src/tracker-store/tracker-statistics.c       |   98 ---
 src/tracker-store/tracker-statistics.h       |   58 --
 src/tracker-store/tracker-statistics.vala    |   46 ++
 src/tracker-store/tracker-status.c           |  231 -------
 src/tracker-store/tracker-status.h           |   77 ---
 src/tracker-store/tracker-status.vala        |  122 ++++
 src/tracker-store/tracker-steroids.c         |  886 --------------------------
 src/tracker-store/tracker-steroids.h         |   57 --
 src/tracker-store/tracker-steroids.vala      |  213 ++++++
 src/tracker-store/tracker-store.c            |  685 --------------------
 src/tracker-store/tracker-store.h            |   92 ---
 src/tracker-store/tracker-store.vala         |  431 +++++++++++++
 src/tracker-store/tracker-writeback.vapi     |   34 +
 31 files changed, 1691 insertions(+), 3825 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 055f458..76ee493 100644
--- a/configure.ac
+++ b/configure.ac
@@ -311,8 +311,6 @@ TRACKER_STORE_REQUIRED="glib-2.0     >= $GLIB_REQUIRED
                         gio-unix-2.0 >= $GLIB_REQUIRED
                         gthread-2.0  >= $GLIB_REQUIRED
                         gmodule-2.0  >= $GLIB_REQUIRED
-                        dbus-1       >= $DBUS_REQUIRED
-                        dbus-glib-1  >= $DBUS_GLIB_REQUIRED
                         sqlite3      >= $SQLITE_REQUIRED"
 
 PKG_CHECK_MODULES(TRACKER_STORE, [$TRACKER_STORE_REQUIRED])
diff --git a/src/tracker-store/.gitignore b/src/tracker-store/.gitignore
index 5b17e28..b57c9e6 100644
--- a/src/tracker-store/.gitignore
+++ b/src/tracker-store/.gitignore
@@ -1 +1,9 @@
 tracker-store
+tracker-backup.c
+tracker-dbus.c
+tracker-resources.c
+tracker-statistics.c
+tracker-status.c
+tracker-steroids.c
+tracker-store.c
+tracker-store.h
diff --git a/src/tracker-store/Makefile.am b/src/tracker-store/Makefile.am
index b113296..d5e4413 100644
--- a/src/tracker-store/Makefile.am
+++ b/src/tracker-store/Makefile.am
@@ -8,7 +8,9 @@ AM_CPPFLAGS =                                          \
 	-DPUSH_MODULES_DIR=\""$(libdir)/tracker-$(TRACKER_API_VERSION)/push-modules/daemon"\" \
 	-I$(top_srcdir)/src                            \
 	-I$(top_builddir)/src                          \
-	$(TRACKER_STORE_CFLAGS)
+	$(TRACKER_STORE_CFLAGS)                        \
+	-include config.h                              \
+	-DNO_LIBDBUS
 
 #
 # Daemon sources
@@ -16,31 +18,42 @@ AM_CPPFLAGS =                                          \
 libexec_PROGRAMS = tracker-store
 
 tracker_store_SOURCES =                                \
-	$(marshal_sources)                             \
-	$(dbus_sources)                                \
-	tracker-backup.c                               \
-	tracker-backup.h                               \
+	tracker-backup.vala                            \
 	tracker-config.c                               \
-	tracker-config.h                               \
-	tracker-dbus.c                                 \
-	tracker-dbus.h                                 \
+	tracker-dbus.vala                              \
 	tracker-events.c                               \
-	tracker-events.h                               \
-	tracker-writeback.c                            \
-	tracker-writeback.h                            \
+	tracker-locale-change.c                        \
 	tracker-main.c                                 \
-	tracker-resources.c                            \
-	tracker-resources.h                            \
-	tracker-statistics.c                           \
-	tracker-statistics.h                           \
-	tracker-store.c                                \
-	tracker-store.h                                \
-	tracker-status.c                               \
-	tracker-status.h                               \
-	tracker-steroids.c                             \
-	tracker-steroids.h                             \
+	tracker-resources.vala                         \
+	tracker-statistics.vala                        \
+	tracker-status.vala                            \
+	tracker-steroids.vala                          \
+	tracker-store.vala                             \
+	tracker-writeback.c
+
+noinst_HEADERS =                                       \
+	tracker-config.h                               \
+	tracker-events.h                               \
 	tracker-locale-change.h                        \
-	tracker-locale-change.c
+	tracker-store.h                                \
+	tracker-writeback.h
+
+BUILT_SOURCES = tracker-store.h
+
+tracker_store_VALAFLAGS = \
+	--pkg gio-2.0 \
+	--pkg gio-unix-2.0 \
+	--pkg posix \
+	$(BUILD_VALAFLAGS) \
+	$(top_srcdir)/src/libtracker-common/libtracker-common.vapi \
+	$(top_srcdir)/src/libtracker-sparql/tracker-sparql-$(TRACKER_API_VERSION).vapi \
+	$(top_srcdir)/src/libtracker-data/tracker-sparql-query.vapi \
+	$(top_srcdir)/src/libtracker-data/libtracker-data.vapi \
+	$(top_srcdir)/src/tracker-store/tracker-config.vapi \
+	$(top_srcdir)/src/tracker-store/tracker-events.vapi \
+	$(top_srcdir)/src/tracker-store/tracker-locale-change.vapi \
+	$(top_srcdir)/src/tracker-store/tracker-writeback.vapi \
+	-H tracker-store.h
 
 tracker_store_LDADD =                                  \
 	$(top_builddir)/src/libtracker-data/libtracker-data.la \
@@ -49,34 +62,8 @@ tracker_store_LDADD =                                  \
 	$(BUILD_LIBS)                                  \
 	$(TRACKER_STORE_LIBS)
 
-marshal_sources =                                      \
-        tracker-marshal.h                              \
-        tracker-marshal.c
-
-dbus_sources =                                         \
-	tracker-backup-glue.h                          \
-	tracker-resources-glue.h                       \
-	tracker-statistics-glue.h                      \
-	tracker-status-glue.h
-
-tracker-marshal.h: tracker-marshal.list
-	$(AM_V_GEN)$(GLIB_GENMARSHAL) $< --prefix=tracker_marshal --header > $@
-
-tracker-marshal.c: tracker-marshal.list
-	$(AM_V_GEN)echo "#include \"tracker-marshal.h\"" > $@ && \
-		   $(GLIB_GENMARSHAL) $< --prefix=tracker_marshal --body >> $@
-
-%-glue.h: $(top_srcdir)/data/dbus/%.xml
-	$(AM_V_GEN)$(DBUSBINDINGTOOL) --mode=glib-server --output=$@ --prefix=$(subst -,_,$*) $^
-
-%-client.h: $(top_srcdir)/data/dbus/%.xml
-	$(AM_V_GEN)$(DBUSBINDINGTOOL) --mode=glib-client --output=$@ --prefix=$(subst -,_,$*) $^
-
-BUILT_SOURCES =                                        \
-	$(marshal_sources)                             \
-	$(dbus_sources)
-
-CLEANFILES = $(BUILT_SOURCES)
-
-EXTRA_DIST = tracker-marshal.list
-
+EXTRA_DIST = \
+	tracker-config.vapi \
+	tracker-events.vapi \
+	tracker-locale-change.vapi \
+	tracker-writeback.vapi
diff --git a/src/tracker-store/tracker-backup.vala b/src/tracker-store/tracker-backup.vala
new file mode 100644
index 0000000..19d0b6c
--- /dev/null
+++ b/src/tracker-store/tracker-backup.vala
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2006, Jamie McCracken <jamiemcc gnome org>
+ * Copyright (C) 2008-2011, Nokia <ivan frade nokia com>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+[DBus (name = "org.freedesktop.Tracker1.Backup")]
+class Tracker.Backup : Object {
+	public const string PATH = "/org/freedesktop/Tracker1/Backup";
+
+	public async void save (BusName sender, string destination_uri) throws Error {
+		NotifyClassGetter getter = null;
+		var resources = (Resources) Tracker.DBus.get_object (typeof (Resources));
+		if (resources != null) {
+			resources.disable_signals ();
+			getter = Tracker.Events.get_class_getter ();
+			Tracker.Events.shutdown ();
+		}
+
+		var request = DBusRequest.begin (sender, "D-Bus request to save backup into '%s'", destination_uri);
+		try {
+			var destination = File.new_for_uri (destination_uri);
+
+			yield Tracker.Store.pause ();
+
+			Error backup_error = null;
+			Data.backup_save (destination, error => {
+				backup_error = error;
+				save.callback ();
+			});
+			yield;
+
+			if (backup_error != null) {
+				throw backup_error;
+			}
+
+			request.end ();
+		} catch (Error e) {
+			request.end (e);
+			throw e;
+		} finally {
+			if (resources != null) {
+				Tracker.Events.init (getter);
+				resources.enable_signals ();
+			}
+
+			Tracker.Store.resume ();
+		}
+	}
+
+	public async void restore (BusName sender, string journal_uri) throws Error {
+		NotifyClassGetter getter = null;
+		var resources = (Resources) Tracker.DBus.get_object (typeof (Resources));
+		if (resources != null) {
+			resources.disable_signals ();
+			getter = Tracker.Events.get_class_getter ();
+			Tracker.Events.shutdown ();
+		}
+
+		var request = DBusRequest.begin (sender, "D-Bus request to restore backup from '%s'", journal_uri);
+		try {
+			yield Tracker.Store.pause ();
+
+			var journal = File.new_for_uri (journal_uri);
+
+			var notifier = (Status) (Tracker.DBus.get_object (typeof (Status)));
+			var busy_callback = notifier.get_callback ();
+
+			Data.backup_restore (journal, null, busy_callback);
+
+			request.end ();
+		} catch (Error e) {
+			request.end (e);
+			throw e;
+		} finally {
+			if (resources != null) {
+				Tracker.Events.init (getter);
+				resources.enable_signals ();
+			}
+
+			Tracker.Store.resume ();
+		}
+	}
+}
diff --git a/src/tracker-store/tracker-config.vapi b/src/tracker-store/tracker-config.vapi
new file mode 100644
index 0000000..72888da
--- /dev/null
+++ b/src/tracker-store/tracker-config.vapi
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011, Nokia <ivan frade nokia com>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+namespace Tracker {
+	[CCode (cheader_filename = "tracker-store/tracker-config.h")]
+	public class Config : ConfigFile {
+		public Config ();
+		public bool save ();
+		public int verbosity { get; set; }
+	}
+}
diff --git a/src/tracker-store/tracker-dbus.vala b/src/tracker-store/tracker-dbus.vala
new file mode 100644
index 0000000..52b0365
--- /dev/null
+++ b/src/tracker-store/tracker-dbus.vala
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2006, Jamie McCracken <jamiemcc gnome org>
+ * Copyright (C) 2008-2011, Nokia <ivan frade nokia com>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+public class Tracker.DBus {
+	static DBusConnection connection;
+
+	const string SERVICE = "org.freedesktop.Tracker1";
+
+	static uint name_owner_changed_id;
+	static Tracker.Statistics statistics;
+	static uint statistics_id;
+	static Tracker.Resources resources;
+	static uint resources_id;
+	static Tracker.Steroids steroids;
+	static uint steroids_id;
+	static Tracker.Status notifier;
+	static uint notifier_id;
+	static Tracker.Backup backup;
+	static uint backup_id;
+
+	static bool dbus_register_service (string name) {
+		message ("Registering D-Bus service...\n  Name:'%s'", name);
+
+		try {
+			Variant reply = connection.call_sync ("org.freedesktop.DBus",
+				"/org/freedesktop/DBus",
+				"org.freedesktop.DBus", "RequestName",
+				new Variant ("(su)", name, 1 << 2 /* DBUS_NAME_FLAG_DO_NOT_QUEUE */),
+				(VariantType) "(u)",
+				0, -1);
+
+			uint result;
+			reply.get ("(u)", out result);
+			if (result != 1 /* DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER */) {
+				critical ("D-Bus service name:'%s' is already taken, " +
+				          "perhaps the daemon is already running?",
+					  name);
+				return false;
+			}
+
+			return true;
+		} catch (Error e) {
+			critical ("Could not aquire name:'%s', %s", name, e.message);
+			return false;
+		}
+	}
+
+	static uint register_object<T> (DBusConnection lconnection, T object, string path) {
+		message ("Registering D-Bus object...");
+		message ("  Path:'%s'", path);
+		message ("  Type:'%s'", typeof (T).name ());
+
+		try {
+			uint id = lconnection.register_object (path, object);
+			return id;
+		} catch (Error e) {
+			critical ("Could not register D-Bus object: %s", e.message);
+			return 0;
+		}
+	}
+
+	public static bool register_names () {
+		/* Register the service name for org.freedesktop.Tracker */
+		if (!dbus_register_service (SERVICE)) {
+			return false;
+		}
+
+		return true;
+	}
+
+	public static bool init () {
+		/* Don't reinitialize */
+		if (connection != null) {
+			return true;
+		}
+
+		try {
+			connection = Bus.get_sync (BusType.SESSION);
+		} catch (Error e) {
+			critical ("Could not connect to the D-Bus session bus, %s", e.message);
+			return false;
+		}
+
+		return true;
+	}
+
+	static void name_owner_changed_cb (DBusConnection connection, string sender_name, string object_path, string interface_name, string signal_name, Variant parameters) {
+
+		unowned string name, old_owner, new_owner;
+		parameters.get ("(&s&s&s)", out name, out old_owner, out new_owner);
+
+		if (old_owner != "" && new_owner == "") {
+			/* This means that old_owner got removed */
+			resources.unreg_batches (old_owner);
+		}
+	}
+
+	static void set_available (bool available) {
+		if (available) {
+			if (resources_id == 0) {
+				register_objects ();
+			}
+		} else {
+			if (resources_id != 0) {
+				connection.signal_unsubscribe (name_owner_changed_id);
+				name_owner_changed_id = 0;
+
+				connection.unregister_object (resources_id);
+				resources = null;
+				resources_id = 0;
+
+				connection.unregister_object (steroids_id);
+				steroids = null;
+				steroids_id = 0;
+			}
+		}
+	}
+
+	public static void shutdown () {
+		set_available (false);
+
+		if (backup != null) {
+			connection.unregister_object (backup_id);
+			backup = null;
+			backup_id = 0;
+		}
+
+		if (notifier != null) {
+			connection.unregister_object (notifier_id);
+			notifier = null;
+			notifier_id = 0;
+		}
+
+		connection = null;
+	}
+
+	public static Tracker.Status? register_notifier () {
+		if (connection == null) {
+			critical ("D-Bus support must be initialized before registering objects!");
+			return null;
+		}
+
+		/* Add org.freedesktop.Tracker */
+		notifier = new Tracker.Status ();
+		if (notifier == null) {
+			critical ("Could not create TrackerStatus object to register");
+			return null;
+		}
+
+		notifier_id = register_object (connection, notifier, Tracker.Status.PATH);
+
+		return notifier;
+	}
+
+	public static bool register_objects () {
+		//gpointer object, resources;
+
+		if (connection == null) {
+			critical ("D-Bus support must be initialized before registering objects!");
+			return false;
+		}
+
+		/* Add org.freedesktop.Tracker.Statistics */
+		statistics = new Tracker.Statistics ();
+		if (statistics == null) {
+			critical ("Could not create TrackerStatistics object to register");
+			return false;
+		}
+
+		statistics_id = register_object (connection, statistics, Tracker.Statistics.PATH);
+
+		/* Add org.freedesktop.Tracker1.Resources */
+		resources = new Tracker.Resources (connection);
+		if (resources == null) {
+			critical ("Could not create TrackerResources object to register");
+			return false;
+		}
+
+		name_owner_changed_id = connection.signal_subscribe ("org.freedesktop.DBus",
+			"org.freedesktop.DBus", "NameOwnerChanged",
+			"/org/freedesktop/DBus",
+			null,
+			0,
+			name_owner_changed_cb);
+
+		resources_id = register_object (connection, resources, Tracker.Resources.PATH);
+
+		/* Add org.freedesktop.Tracker1.Steroids */
+		steroids = new Tracker.Steroids ();
+		if (steroids == null) {
+			critical ("Could not create TrackerSteroids object to register");
+			return false;
+		}
+
+		steroids_id = register_object (connection, steroids, Tracker.Steroids.PATH);
+
+		if (backup == null) {
+			/* Add org.freedesktop.Tracker1.Backup */
+			backup = new Tracker.Backup ();
+			if (backup == null) {
+				critical ("Could not create TrackerBackup object to register");
+				return false;
+			}
+
+			backup_id = register_object (connection, backup, Tracker.Backup.PATH);
+		}
+
+		return true;
+	}
+
+	public static bool register_prepare_class_signal () {
+		if (resources == null) {
+			message ("Error during initialization, Resources DBus object not available");
+			return false;
+		}
+
+		resources.enable_signals ();
+
+		return true;
+	}
+
+	public static Object? get_object (Type type) {
+		if (type == typeof (Resources)) {
+			return resources;
+		}
+
+		if (type == typeof (Steroids)) {
+			return steroids;
+		}
+
+		if (type == typeof (Status)) {
+			return notifier;
+		}
+
+		if (type == typeof (Backup)) {
+			return backup;
+		}
+
+		return null;
+	}
+}
diff --git a/src/tracker-store/tracker-events.vapi b/src/tracker-store/tracker-events.vapi
new file mode 100644
index 0000000..23bd415
--- /dev/null
+++ b/src/tracker-store/tracker-events.vapi
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011, Nokia <ivan frade nokia com>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+namespace Tracker {
+	[CCode (has_array_length = false, array_null_terminated = true, has_target = false, cheader_filename = "tracker-store/tracker-events.h")]
+	public delegate string[] NotifyClassGetter ();
+
+	[CCode (cheader_filename = "tracker-store/tracker-events.h")]
+	namespace Events {
+		public void init (NotifyClassGetter callback);
+		public NotifyClassGetter get_class_getter ();
+		public void shutdown ();
+		public void add_insert (int graph_id, int subject_id, string subject, int pred_id, int object_id, string object, GLib.PtrArray rdf_types);
+		public void add_delete (int graph_id, int subject_id, string subject, int pred_id, int object_id, string object, GLib.PtrArray rdf_types);
+		public GLib.HashTableIter<Tracker.Class, bool> classes_iter ();
+		public uint get_total (bool and_reset);
+		public void reset_pending ();
+		public void freeze ();
+	}
+}
diff --git a/src/tracker-store/tracker-locale-change.c b/src/tracker-store/tracker-locale-change.c
index 5a7495c..7c43f98 100644
--- a/src/tracker-store/tracker-locale-change.c
+++ b/src/tracker-store/tracker-locale-change.c
@@ -32,8 +32,6 @@
 #include <libtracker-data/tracker-db-dbus.h>
 
 #include "tracker-store.h"
-#include "tracker-dbus.h"
-#include "tracker-resources.h"
 #include "tracker-events.h"
 #include "tracker-locale-change.h"
 
@@ -47,17 +45,21 @@ static gpointer locale_notification_id;
 static gboolean locale_change_notified;
 
 static void
-locale_change_process_cb (gpointer user_data)
+locale_change_process_cb (GObject      *source,
+                          GAsyncResult *res,
+                          gpointer      user_data)
 {
 	TrackerStatus *notifier;
 	TrackerBusyCallback busy_callback;
 	gpointer busy_user_data;
+	GDestroyNotify busy_destroy_notify;
 	TrackerLocaleChangeContext *ctxt = user_data;
 
 	notifier = TRACKER_STATUS (tracker_dbus_get_object (TRACKER_TYPE_STATUS));
 
 	busy_callback = tracker_status_get_callback (notifier,
-	                                             &busy_user_data);
+	                                             &busy_user_data,
+	                                             &busy_destroy_notify);
 
 	g_message ("Processing locale change...");
 	/* Reload! This will regenerate indexes with the new locale */
@@ -65,6 +67,8 @@ locale_change_process_cb (gpointer user_data)
 	                             busy_user_data,
 	                             "Changing locale");
 
+	busy_destroy_notify (busy_user_data);
+
 	if (ctxt->resources) {
 		tracker_events_init (ctxt->getter);
 		tracker_resources_enable_signals (ctxt->resources);
@@ -72,7 +76,7 @@ locale_change_process_cb (gpointer user_data)
 	}
 	g_free (ctxt);
 
-	tracker_store_set_active (TRUE, FALSE, NULL);
+	tracker_store_resume ();
 
 	locale_change_notified = FALSE;
 }
@@ -94,7 +98,7 @@ locale_change_process_idle_cb (gpointer data)
 	/* Note: Right now, the passed callback may be called instantly and not
 	 * in an idle. */
 	g_message ("Setting tracker-store as inactive...");
-	tracker_store_set_active (FALSE, locale_change_process_cb, ctxt);
+	tracker_store_pause (locale_change_process_cb, ctxt);
 
 	return FALSE;
 }
diff --git a/src/tracker-store/tracker-locale-change.vapi b/src/tracker-store/tracker-locale-change.vapi
new file mode 100644
index 0000000..afc2153
--- /dev/null
+++ b/src/tracker-store/tracker-locale-change.vapi
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2011, Nokia <ivan frade nokia com>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+namespace Tracker {
+	[CCode (cheader_filename = "tracker-store/tracker-locale-change.h")]
+	public void locale_change_initialize_subscription ();
+	[CCode (cheader_filename = "tracker-store/tracker-locale-change.h")]
+	public void locale_change_shutdown_subscription ();
+}
diff --git a/src/tracker-store/tracker-main.c b/src/tracker-store/tracker-main.c
index d21136b..5e10240 100644
--- a/src/tracker-store/tracker-main.c
+++ b/src/tracker-store/tracker-main.c
@@ -50,14 +50,11 @@
 #include <libtracker-data/tracker-db-dbus.h>
 #include <libtracker-data/tracker-db-manager.h>
 
-#include "tracker-dbus.h"
 #include "tracker-config.h"
 #include "tracker-events.h"
-#include "tracker-writeback.h"
-#include "tracker-backup.h"
-#include "tracker-store.h"
-#include "tracker-statistics.h"
 #include "tracker-locale-change.h"
+#include "tracker-store.h"
+#include "tracker-writeback.h"
 
 #define ABOUT	  \
 	"Tracker " PACKAGE_VERSION "\n"
@@ -296,6 +293,7 @@ main (gint argc, gchar *argv[])
 	TrackerStatus *notifier;
 	gpointer busy_user_data;
 	TrackerBusyCallback busy_callback;
+	GDestroyNotify busy_destroy_notify;
 	gint chunk_size_mb;
 	gsize chunk_size;
 	const gchar *rotate_to;
@@ -316,8 +314,6 @@ main (gint argc, gchar *argv[])
 	                      private,
 	                      private_free);
 
-	dbus_g_thread_init ();
-
 	setlocale (LC_ALL, "");
 
 	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
@@ -398,7 +394,8 @@ main (gint argc, gchar *argv[])
 
 	notifier = tracker_dbus_register_notifier ();
 	busy_callback = tracker_status_get_callback (notifier,
-	                                            &busy_user_data);
+	                                            &busy_user_data,
+	                                            &busy_destroy_notify);
 
 	tracker_store_init ();
 
@@ -474,7 +471,7 @@ main (gint argc, gchar *argv[])
 	tracker_events_init (get_notifiable_classes);
 	tracker_writeback_init (get_writeback_predicates);
 
-	tracker_store_set_active (TRUE, NULL, NULL);
+	tracker_store_resume ();
 
 	g_message ("Waiting for D-Bus requests...");
 
diff --git a/src/tracker-store/tracker-resources.vala b/src/tracker-store/tracker-resources.vala
new file mode 100644
index 0000000..2c8bc7c
--- /dev/null
+++ b/src/tracker-store/tracker-resources.vala
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2006, Jamie McCracken <jamiemcc gnome org>
+ * Copyright (C) 2008-2011, Nokia <ivan frade nokia com>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+[DBus (name = "org.freedesktop.Tracker1.Resources")]
+public class Tracker.Resources : Object {
+	public const string PATH = "/org/freedesktop/Tracker1/Resources";
+
+	const int GRAPH_UPDATED_IMMEDIATE_EMIT_AT = 1000;
+	const int SIGNALS_SECONDS_PER_EMIT = 1;
+
+	/* I *know* that this is some arbitrary number that doesn't seem to
+	 * resemble anything. In fact it's what I experimentally measured to
+	 * be a good value on a default Debian testing which has
+	 * max_message_size set to 1 000 000 000 in session.conf. I didn't have
+	 * the feeling that this value was very much respected, as the size
+	 * of the DBusMessage when libdbus decided to exit() the process was
+	 * around 160 MB, and not ~ 1000 MB. So if you take 160 MB and you
+	 * devide it by 1000000 you have an average string size of ~ 160
+	 * bytes plus DBusMessage's overhead. If that makes this number less
+	 * arbitrary for you, then fine.
+	 *
+	 * I really hope that the libdbus people get to their senses and
+	 * either stop doing their exit() nonsense in a library, and instead
+	 * return a clean DBusError or something, or create crystal clear
+	 * clarity about the maximum size of a message. And make it both so
+	 * that I can get this length at runtime (without having to parse
+	 * libdbus's own configuration files) and my DBusMessage's current
+	 * total length. As far as I know are both not possible. So that for
+	 * me means that libdbus's exit() is unacceptable.
+	 *
+	 * Note for the debugger of the future, the "Disconnected" signal gets
+	 * sent to us by the bus, which in turn makes libdbus-glib perform exit(). */
+
+	const int DBUS_ARBITRARY_MAX_MSG_SIZE = 1000000;
+
+	DBusConnection connection;
+	uint signal_timeout;
+
+	public signal void writeback ([DBus (signature = "a{iai}")] Variant subjects);
+	public signal void graph_updated (string classname, [DBus (signature = "a(iiii)")] Variant deletes, [DBus (signature = "a(iiii)")] Variant inserts);
+
+	public Resources (DBusConnection connection) {
+		this.connection = connection;
+	}
+
+	public async void load (BusName sender, string uri) throws Error {
+		var request = DBusRequest.begin (sender, "Resources.Load (uri: '%s')", uri);
+		try {
+			var file = File.new_for_uri (uri);
+
+			yield Tracker.Store.queue_turtle_import (file, sender);
+
+			request.end ();
+		} catch (Error e) {
+			request.end (e);
+			throw e;
+		}
+	}
+
+	[DBus (signature = "aas")]
+	public async Variant sparql_query (BusName sender, string query) throws Error {
+		var request = DBusRequest.begin (sender, "Resources.SparqlQuery");
+		request.debug ("query: %s", query);
+		try {
+			var builder = new VariantBuilder ((VariantType) "aas");
+
+			yield Tracker.Store.sparql_query (query, Tracker.Store.Priority.HIGH, cursor => {
+				while (cursor.next ()) {
+					builder.open ((VariantType) "as");
+
+					for (int i = 0; i < cursor.n_columns; i++) {
+						unowned string str = cursor.get_string (i);
+
+						if (str == null) {
+							str = "";
+						}
+
+						builder.add ("s", str);
+					}
+
+					builder.close ();
+				}
+			}, sender);
+
+			request.end ();
+
+			var result = builder.end ();
+			if (result.get_size () > DBUS_ARBITRARY_MAX_MSG_SIZE) {
+				throw new DBusError.FAILED ("result set of the query is too large");
+			} else {
+				return result;
+			}
+		} catch (Error e) {
+			request.end (e);
+			throw e;
+		}
+	}
+
+	public async void sparql_update (BusName sender, string update) throws Error {
+		var request = DBusRequest.begin (sender, "Resources.SparqlUpdate");
+		request.debug ("query: %s", update);
+		try {
+			yield Tracker.Store.sparql_update (update, Tracker.Store.Priority.HIGH, sender);
+
+			request.end ();
+		} catch (Error e) {
+			request.end (e);
+			throw e;
+		}
+	}
+
+	[DBus (signature = "aaa{ss}")]
+	public async Variant sparql_update_blank (BusName sender, string update) throws Error {
+		var request = DBusRequest.begin (sender, "Resources.SparqlUpdateBlank");
+		request.debug ("query: %s", update);
+		try {
+			var blank_nodes = yield Tracker.Store.sparql_update_blank (update, Tracker.Store.Priority.HIGH, sender);
+
+			request.end ();
+
+			var builder = new VariantBuilder ((VariantType) "aaa{ss}");
+
+			for (int i = 0; i < blank_nodes.length; i++) {
+				var inner_array = blank_nodes[i];
+
+				builder.open ((VariantType) "aa{ss}");
+				for (int j = 0; j < inner_array.length; j++) {
+					builder.add_value (inner_array[j]);
+				}
+				builder.close ();
+			}
+
+			return builder.end ();
+		} catch (Error e) {
+			request.end (e);
+			throw e;
+		}
+	}
+
+	public void sync (BusName sender) {
+		var request = DBusRequest.begin (sender, "Resources.Sync");
+
+		Data.sync ();
+
+		request.end ();
+	}
+
+	public async void batch_sparql_update (BusName sender, string update) throws Error {
+		var request = DBusRequest.begin (sender, "Resources.BatchSparqlUpdate");
+		request.debug ("query: %s", update);
+		try {
+			yield Tracker.Store.sparql_update (update, Tracker.Store.Priority.LOW, sender);
+
+			request.end ();
+		} catch (Error e) {
+			request.end (e);
+			throw e;
+		}
+	}
+
+	public void batch_commit () {
+		/* no longer needed, just return */
+	}
+
+	bool emit_graph_updated (Class cl) {
+		if (cl.has_insert_events () || cl.has_delete_events ()) {
+			var builder = new VariantBuilder ((VariantType) "a(iiii)");
+			cl.foreach_delete_event ((graph_id, subject_id, pred_id, object_id) => {
+				builder.add ("(iiii)", graph_id, subject_id, pred_id, object_id);
+			});
+			var deletes = builder.end ();
+
+			builder = new VariantBuilder ((VariantType) "a(iiii)");
+			cl.foreach_insert_event ((graph_id, subject_id, pred_id, object_id) => {
+				builder.add ("(iiii)", graph_id, subject_id, pred_id, object_id);
+			});
+			var inserts = builder.end ();
+
+			graph_updated (cl.uri, deletes, inserts);
+
+			cl.reset_ready_events ();
+
+			return true;
+		}
+		return false;
+	}
+
+	bool on_emit_signals () {
+		bool had_any = false;
+
+		/* Class signal feature */
+		var iter = Tracker.Events.classes_iter ();
+
+		unowned Class cl;
+		bool value;
+		while (iter.next (out cl, out value)) {
+			if (emit_graph_updated (cl)) {
+				had_any = true;
+			}
+		}
+
+		/* Reset counter */
+		Tracker.Events.get_total (true);
+
+
+		/* Writeback feature */
+		var writebacks = Tracker.Writeback.get_ready ();
+
+		if (writebacks != null) {
+			had_any = true;
+			var builder = new VariantBuilder ((VariantType) "a{iai}");
+
+			var wb_iter = HashTableIter<int, GLib.Array<int>> (writebacks);
+
+			int subject_id;
+			unowned Array<int> types;
+			while (wb_iter.next (out subject_id, out types)) {
+				builder.open ((VariantType) "{iai}");
+
+				builder.add ("i", subject_id);
+
+				builder.open ((VariantType) "ai");
+				for (int i = 0; i < types.length; i++) {
+					builder.add ("i", types.index (i));
+				}
+				builder.close ();
+
+				builder.close ();
+			}
+
+			writeback (builder.end ());
+		}
+
+		Tracker.Writeback.reset_ready ();
+
+		if (!had_any) {
+			signal_timeout = 0;
+		}
+
+		return had_any;
+	}
+
+	void on_statements_committed (bool start_timer) {
+		/* Class signal feature */
+
+		var iter = Tracker.Events.classes_iter ();
+
+		unowned Class cl;
+		bool value;
+		while (iter.next (out cl, out value)) {
+			cl.transact_events ();
+		}
+
+		if (start_timer && signal_timeout == 0) {
+			signal_timeout = Timeout.add_seconds (SIGNALS_SECONDS_PER_EMIT, on_emit_signals);
+		}
+
+		/* Writeback feature */
+		Tracker.Writeback.transact ();
+	}
+
+	void on_statements_rolled_back (bool start_timer) {
+		Tracker.Events.reset_pending ();
+		Tracker.Writeback.reset_pending ();
+	}
+
+	void check_graph_updated_signal () {
+		/* Check for whether we need an immediate emit */
+		if (Tracker.Events.get_total (false) > GRAPH_UPDATED_IMMEDIATE_EMIT_AT) {
+			var iter = Tracker.Events.classes_iter ();
+
+			unowned Class cl;
+			bool value;
+			while (iter.next (out cl, out value)) {
+				emit_graph_updated (cl);
+			}
+
+			/* Reset counter */
+			Tracker.Events.get_total (true);
+		}
+	}
+
+	void on_statement_inserted (int graph_id, string? graph, int subject_id, string subject, int pred_id, int object_id, string object, PtrArray rdf_types) {
+		Tracker.Events.add_insert (graph_id, subject_id, subject, pred_id, object_id, object, rdf_types);
+		Tracker.Writeback.check (graph_id, graph, subject_id, subject, pred_id, object_id, object, rdf_types);
+		check_graph_updated_signal ();
+	}
+
+	void on_statement_deleted (int graph_id, string? graph, int subject_id, string subject, int pred_id, int object_id, string object, PtrArray rdf_types) {
+		Tracker.Events.add_delete (graph_id, subject_id, subject, pred_id, object_id, object, rdf_types);
+		Tracker.Writeback.check (graph_id, graph, subject_id, subject, pred_id, object_id, object, rdf_types);
+		check_graph_updated_signal ();
+	}
+
+	public void enable_signals () {
+		Tracker.Data.add_insert_statement_callback (on_statement_inserted);
+		Tracker.Data.add_delete_statement_callback (on_statement_deleted);
+		Tracker.Data.add_commit_statement_callback (on_statements_committed);
+		Tracker.Data.add_rollback_statement_callback (on_statements_rolled_back);
+	}
+
+	public void disable_signals () {
+		Tracker.Data.remove_insert_statement_callback (on_statement_inserted);
+		Tracker.Data.remove_delete_statement_callback (on_statement_deleted);
+		Tracker.Data.remove_commit_statement_callback (on_statements_committed);
+		Tracker.Data.remove_rollback_statement_callback (on_statements_rolled_back);
+
+		if (signal_timeout != 0) {
+			Source.remove (signal_timeout);
+		}
+	}
+
+	~Resources () {
+		this.disable_signals ();
+	}
+
+	public void unreg_batches (string old_owner) {
+		Tracker.Store.unreg_batches (old_owner);
+	}
+}
diff --git a/src/tracker-store/tracker-statistics.vala b/src/tracker-store/tracker-statistics.vala
new file mode 100644
index 0000000..b80d2bf
--- /dev/null
+++ b/src/tracker-store/tracker-statistics.vala
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011, Nokia <ivan frade nokia com>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+[DBus (name = "org.freedesktop.Tracker1.Statistics")]
+public class Tracker.Statistics : Object {
+	public const string PATH = "/org/freedesktop/Tracker1/Statistics";
+
+	[DBus (signature = "aas")]
+	public new Variant get (BusName sender) {
+		var request = DBusRequest.begin (sender, "Statistics.Get");
+
+		var builder = new VariantBuilder ((VariantType) "aas");
+
+		foreach (var cl in Ontologies.get_classes ()) {
+			if (cl.count == 0) {
+				/* skip classes without resources */
+				continue;
+			}
+
+			builder.open ((VariantType) "as");
+			builder.add ("s", cl.name);
+			builder.add ("s", cl.count.to_string ());
+			builder.close ();
+		}
+
+		request.end ();
+
+		return builder.end ();
+	}
+}
diff --git a/src/tracker-store/tracker-status.vala b/src/tracker-store/tracker-status.vala
new file mode 100644
index 0000000..430c802
--- /dev/null
+++ b/src/tracker-store/tracker-status.vala
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008-2011, Nokia <ivan frade nokia com>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ *
+ * Authors:
+ *  Philip Van Hoof <philip codeminded be>
+ */
+
+[DBus (name = "org.freedesktop.Tracker1.Status")]
+public class Tracker.Status : Object {
+	public const string PATH = "/org/freedesktop/Tracker1/Status";
+
+	const int PROGRESS_TIMEOUT_S = 5;
+
+	class WaitContext : Object {
+		public SourceFunc callback;
+	}
+
+	double _progress;
+	string status = "Idle";
+	uint timer_id;
+	List<WaitContext> wait_list;
+
+	/**
+	 * TrackerStatus::progress:
+	 * @notifier: the TrackerStatus
+	 * @status: store status
+	 * @progress: a #gdouble indicating store progress, from 0 to 1.
+	 *
+	 * the ::progress signal will be emitted by TrackerStatus
+	 * to indicate progress about the store process. @status will
+	 * contain a translated string with the current status and @progress
+	 * will indicate how much has been processed so far.
+	 **/
+	public signal void progress (string status, double progres);
+
+	~Status () {
+		if (timer_id != 0) {
+			Source.remove (timer_id);
+		}
+	}
+
+	bool
+	busy_notification_timeout () {
+		progress (status, _progress);
+
+		timer_id = 0;
+
+		return false;
+	}
+
+	static bool first_time = true;
+
+	void callback (string status, double progress) {
+		this._progress = progress;
+
+		if (progress == 1 && wait_list != null) {
+			/* notify clients that tracker-store is no longer busy */
+
+			wait_list.reverse ();
+			foreach (var context in wait_list) {
+				context.callback ();
+			}
+
+			wait_list = null;
+		}
+
+		if (status != this.status) {
+			this.status = status;
+		}
+
+		if (timer_id == 0) {
+			if (first_time) {
+				this.timer_id = Idle.add (busy_notification_timeout);
+				first_time = false;
+			} else {
+				timer_id = Timeout.add_seconds (PROGRESS_TIMEOUT_S, busy_notification_timeout);
+			}
+		}
+
+		while (MainContext.default ().iteration (false)) {
+		}
+	}
+
+	[DBus (visible = false)]
+	public BusyCallback get_callback () {
+		return callback;
+	}
+
+	public double get_progress  () {
+		return this._progress;
+	}
+
+	public string get_status  () {
+		return this.status;
+	}
+
+	public async void wait () throws Error {
+		if (_progress == 1) {
+			/* tracker-store is idle */
+		} else {
+			var context = new WaitContext ();
+			context.callback = wait.callback;
+			wait_list.prepend (context);
+			yield;
+		}
+	}
+}
diff --git a/src/tracker-store/tracker-steroids.vala b/src/tracker-store/tracker-steroids.vala
new file mode 100644
index 0000000..0a9b109
--- /dev/null
+++ b/src/tracker-store/tracker-steroids.vala
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2010, Codeminded BVBA <abustany gnome org>
+ * Copyright (C) 2011, Nokia <ivan frade nokia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+[DBus (name = "org.freedesktop.Tracker1.Steroids")]
+public class Tracker.Steroids : Object {
+	public const string PATH = "/org/freedesktop/Tracker1/Steroids";
+
+	public const int BUFFER_SIZE = 65536;
+
+	public async string[] query (BusName sender, string query, UnixOutputStream output_stream) throws Error {
+		var request = DBusRequest.begin (sender, "Steroids.Query");
+		request.debug ("query: %s", query);
+		try {
+			string[] variable_names = null;
+
+			yield Tracker.Store.sparql_query (query, Tracker.Store.Priority.HIGH, cursor => {
+				var data_output_stream = new DataOutputStream (new BufferedOutputStream.sized (output_stream, BUFFER_SIZE));
+				data_output_stream.set_byte_order (DataStreamByteOrder.HOST_ENDIAN);
+
+				int n_columns = cursor.n_columns;
+
+				int[] column_sizes = new int[n_columns];
+				int[] column_offsets = new int[n_columns];
+				string[] column_data = new string[n_columns];
+
+				variable_names = new string[n_columns];
+				for (int i = 0; i < n_columns; i++) {
+					variable_names[i] = cursor.get_variable_name (i);
+				}
+
+				while (cursor.next ()) {
+					int last_offset = -1;
+
+					for (int i = 0; i < n_columns ; i++) {
+						unowned string str = cursor.get_string (i);
+
+						column_sizes[i] = str != null ? str.length : 0;
+						column_data[i]  = str;
+
+						last_offset += column_sizes[i] + 1;
+						column_offsets[i] = last_offset;
+					}
+
+					data_output_stream.put_int32 (n_columns);
+
+					for (int i = 0; i < n_columns ; i++) {
+						/* Cast from enum to int */
+						data_output_stream.put_int32 ((int) cursor.get_value_type (i));
+					}
+
+					for (int i = 0; i < n_columns ; i++) {
+						data_output_stream.put_int32 (column_offsets[i]);
+					}
+
+					for (int i = 0; i < n_columns ; i++) {
+						data_output_stream.put_string (column_data[i] != null ? column_data[i] : "");
+						data_output_stream.put_byte (0);
+					}
+				}
+			}, sender);
+
+			request.end ();
+
+			return variable_names;
+		} catch (Error e) {
+			request.end (e);
+			throw e;
+		}
+	}
+
+	async Variant? update_internal (BusName sender, Tracker.Store.Priority priority, bool blank, UnixInputStream input_stream) throws Error {
+		var request = DBusRequest.begin (sender,
+			"Steroids.%sUpdate%s",
+			priority != Tracker.Store.Priority.HIGH ? "Batch" : "",
+			blank ? "Blank" : "");
+		try {
+			size_t bytes_read;
+
+			var data_input_stream = new DataInputStream (input_stream);
+			data_input_stream.set_buffer_size (BUFFER_SIZE);
+			data_input_stream.set_byte_order (DataStreamByteOrder.HOST_ENDIAN);
+
+			int query_size = data_input_stream.read_int32 (null);
+
+			/* We malloc one more char to ensure string is 0 terminated */
+			uint8[] query = new uint8[query_size + 1];
+
+			data_input_stream.read_all (query[0:query_size], out bytes_read);
+
+			data_input_stream = null;
+			input_stream = null;
+
+			request.debug ("query: %s", (string) query);
+
+			if (!blank) {
+				yield Tracker.Store.sparql_update ((string) query, priority, sender);
+
+				request.end ();
+
+				return null;
+			} else {
+				var blank_nodes = yield Tracker.Store.sparql_update_blank ((string) query, priority, sender);
+
+				request.end ();
+
+				var builder = new VariantBuilder ((VariantType) "aaa{ss}");
+
+				for (int i = 0; i < blank_nodes.length; i++) {
+					var inner_array = blank_nodes[i];
+
+					builder.open ((VariantType) "aa{ss}");
+					for (int j = 0; j < inner_array.length; j++) {
+						builder.add_value (inner_array[j]);
+					}
+					builder.close ();
+				}
+
+				return builder.end ();
+			}
+		} catch (Error e) {
+			request.end (e);
+			throw e;
+		}
+	}
+
+	public async void update (BusName sender, UnixInputStream input_stream) throws Error {
+		yield update_internal (sender, Tracker.Store.Priority.HIGH, false, input_stream);
+	}
+
+	public async void batch_update (BusName sender, UnixInputStream input_stream) throws Error {
+		yield update_internal (sender, Tracker.Store.Priority.LOW, false, input_stream);
+	}
+
+	[DBus (signature = "aaa{ss}")]
+	public async Variant update_blank (BusName sender, UnixInputStream input_stream) throws Error {
+		return yield update_internal (sender, Tracker.Store.Priority.HIGH, true, input_stream);
+	}
+
+	[DBus (signature = "aaa{ss}")]
+	public async Variant batch_update_blank (BusName sender, UnixInputStream input_stream) throws Error {
+		return yield update_internal (sender, Tracker.Store.Priority.LOW, true, input_stream);
+	}
+
+	[DBus (signature = "as")]
+	public async Variant update_array (BusName sender, UnixInputStream input_stream) throws Error {
+		var request = DBusRequest.begin (sender, "Steroids.UpdateArray");
+		try {
+			var data_input_stream = new DataInputStream (input_stream);
+			data_input_stream.set_buffer_size (BUFFER_SIZE);
+			data_input_stream.set_byte_order (DataStreamByteOrder.HOST_ENDIAN);
+
+			int query_count = data_input_stream.read_int32 ();
+
+			string[] query_array = new string[query_count];
+
+			int i;
+			for (i = 0; i < query_count; i++) {
+				size_t bytes_read;
+
+				int query_size = data_input_stream.read_int32 ();
+
+				/* We malloc one more char to ensure string is 0 terminated */
+				query_array[i] = (string) new uint8[query_size + 1];
+
+				data_input_stream.read_all (((uint8[]) query_array[i])[0:query_size], out bytes_read);
+
+			}
+
+			data_input_stream = null;
+			input_stream = null;
+
+			var builder = new VariantBuilder ((VariantType) "as");
+
+			for (i = 0; i < query_count; i++) {
+				request.debug ("query: %s", query_array[i]);
+
+				try {
+					yield Tracker.Store.sparql_update (query_array[i], Tracker.Store.Priority.HIGH, sender);
+					builder.add ("s", "");
+					builder.add ("s", "");
+				} catch (Error e1) {
+					builder.add ("s", "org.freedesktop.Tracker1.SparqlError.Internal");
+					builder.add ("s", e1.message);
+				}
+
+			}
+
+			request.end ();
+
+			return builder.end ();
+		} catch (Error e) {
+			request.end (e);
+			throw e;
+		}
+	}
+}
diff --git a/src/tracker-store/tracker-store.vala b/src/tracker-store/tracker-store.vala
new file mode 100644
index 0000000..19828bc
--- /dev/null
+++ b/src/tracker-store/tracker-store.vala
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2009-2011, Nokia <ivan frade nokia com>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ *
+ * Author: Philip Van Hoof <philip codeminded be>
+ */
+
+public class Tracker.Store {
+	const int MAX_CONCURRENT_QUERIES = 2;
+
+	const int MAX_TASK_TIME = 30;
+
+	static Queue<Task> query_queues[3 /* TRACKER_STORE_N_PRIORITIES */];
+	static Queue<Task> update_queues[3 /* TRACKER_STORE_N_PRIORITIES */];
+	static int n_queries_running;
+	static bool update_running;
+	static ThreadPool<Task> update_pool;
+	static ThreadPool<Task> query_pool;
+	static GenericArray<Task> running_tasks;
+	static int max_task_time;
+	static bool active;
+	static SourceFunc active_callback;
+
+	public enum Priority {
+		HIGH,
+		LOW,
+		TURTLE,
+		N_PRIORITIES
+	}
+
+	enum TaskType {
+		QUERY,
+		UPDATE,
+		UPDATE_BLANK,
+		TURTLE,
+	}
+
+	public delegate void SparqlQueryInThread (DBCursor cursor) throws Error;
+
+	abstract class Task {
+		public TaskType type;
+		public string client_id;
+		public Error error;
+		public SourceFunc callback;
+	}
+
+	class QueryTask : Task {
+		public string query;
+		public Cancellable cancellable;
+		public uint watchdog_id;
+		public SparqlQueryInThread in_thread;
+
+		~QueryTask () {
+			if (watchdog_id > 0) {
+				Source.remove (watchdog_id);
+			}
+		}
+	}
+
+	class UpdateTask : Task {
+		public string query;
+		public GenericArray<GenericArray<HashTable<string,string>>> blank_nodes;
+		public Priority priority;
+	}
+
+	class TurtleTask : Task {
+		public string path;
+	}
+
+	static void sched () {
+		Task task = null;
+
+		if (!active) {
+			return;
+		}
+
+		while (n_queries_running < MAX_CONCURRENT_QUERIES) {
+			for (int i = 0; i < Priority.N_PRIORITIES; i++) {
+				task = query_queues[i].pop_head ();
+				if (task != null) {
+					break;
+				}
+			}
+			if (task == null) {
+				/* no pending query */
+				break;
+			}
+			running_tasks.add (task);
+
+			if (max_task_time != 0) {
+				var query_task = (QueryTask) task;
+				query_task.watchdog_id = Timeout.add_seconds (max_task_time, () => {
+					query_task.cancellable.cancel ();
+					return false;
+				});
+			}
+
+			n_queries_running++;
+			try {
+				query_pool.push (task);
+			} catch (Error e) {
+				// ignore harmless thread creation error
+			}
+		}
+
+		if (!update_running) {
+			for (int i = 0; i < Priority.N_PRIORITIES; i++) {
+				task = update_queues[i].pop_head ();
+				if (task != null) {
+					break;
+				}
+			}
+			if (task != null) {
+				update_running = true;
+				try {
+					update_pool.push (task);
+				} catch (Error e) {
+					// ignore harmless thread creation error
+				}
+			}
+		}
+	}
+
+	static bool start_timer_or_not (Task task) {
+		bool result;
+
+		switch (task.type) {
+			case TaskType.UPDATE:
+			case TaskType.UPDATE_BLANK:
+				result = !(((UpdateTask) task).priority == Priority.LOW && update_queues[Priority.LOW].get_length () > 0);
+				break;
+			case TaskType.TURTLE:
+				result = update_queues[Priority.TURTLE].get_length () == 0;
+				break;
+			case TaskType.QUERY:
+			default:
+				result = false;
+				break;
+		}
+
+		return result;
+	}
+
+	static bool task_finish_cb (Task task) {
+		if (task.type == TaskType.QUERY) {
+			var query_task = (QueryTask) task;
+
+			if (task.error == null) {
+				try {
+					query_task.cancellable.set_error_if_cancelled ();
+				} catch (Error e) {
+					task.error = e;
+				}
+			}
+
+			task.callback ();
+			task.error = null;
+
+			running_tasks.remove (task);
+			n_queries_running--;
+		} else if (task.type == TaskType.UPDATE || task.type == TaskType.UPDATE_BLANK) {
+			if (task.error == null) {
+				Tracker.Data.notify_transaction (start_timer_or_not (task));
+			}
+
+			task.callback ();
+			task.error = null;
+
+			update_running = false;
+		} else if (task.type == TaskType.TURTLE) {
+			if (task.error == null) {
+				Tracker.Data.notify_transaction (start_timer_or_not (task));
+			}
+
+			task.callback ();
+			task.error = null;
+
+			update_running = false;
+		}
+
+		if (n_queries_running == 0 && !update_running && active_callback != null) {
+			active_callback ();
+		}
+
+		sched ();
+
+		return false;
+	}
+
+	static void pool_dispatch_cb (Task task) {
+		try {
+			if (task.type == TaskType.QUERY) {
+				var query_task = (QueryTask) task;
+
+				var cursor = Tracker.Data.query_sparql_cursor (query_task.query);
+
+				query_task.in_thread (cursor);
+			} else if (task.type == TaskType.UPDATE) {
+				var update_task = (UpdateTask) task;
+
+				Tracker.Data.update_sparql (update_task.query);
+			} else if (task.type == TaskType.UPDATE_BLANK) {
+				var update_task = (UpdateTask) task;
+
+				update_task.blank_nodes = Tracker.Data.update_sparql_blank (update_task.query);
+			} else if (task.type == TaskType.TURTLE) {
+				var turtle_task = (TurtleTask) task;
+
+				var file = File.new_for_path (turtle_task.path);
+
+				Tracker.Events.freeze ();
+				Tracker.Data.load_turtle_file (file);
+				Tracker.Events.reset_pending ();
+			}
+		} catch (Error e) {
+			task.error = e;
+		}
+
+		Idle.add (() => {
+			task_finish_cb (task);
+			return false;
+		});
+	}
+
+	public static void init () {
+		string max_task_time_env = Environment.get_variable ("TRACKER_STORE_MAX_TASK_TIME");
+		if (max_task_time_env != null) {
+			max_task_time = max_task_time_env.to_int ();
+		} else {
+			max_task_time = MAX_TASK_TIME;
+		}
+
+		running_tasks = new GenericArray<Task> ();
+
+		for (int i = 0; i < Priority.N_PRIORITIES; i++) {
+			query_queues[i] = new Queue<Task> ();
+			update_queues[i] = new Queue<Task> ();
+		}
+
+		try {
+			update_pool = new ThreadPool<Task> (pool_dispatch_cb, 1, true);
+			query_pool = new ThreadPool<Task> (pool_dispatch_cb, MAX_CONCURRENT_QUERIES, true);
+		} catch (Error e) {
+			warning (e.message);
+		}
+
+		/* as the following settings are global for unknown reasons,
+		   let's use the same settings as gio, otherwise the used settings
+		   are rather random */
+		ThreadPool.set_max_idle_time (15 * 1000);
+		ThreadPool.set_max_unused_threads (2);
+	}
+
+	public static void shutdown () {
+		query_pool = null;
+		update_pool = null;
+
+		for (int i = 0; i < Priority.N_PRIORITIES; i++) {
+			query_queues[i] = null;
+			update_queues[i] = null;
+		}
+	}
+
+	public static async void sparql_query (string sparql, Priority priority, SparqlQueryInThread in_thread, string client_id) throws Error {
+		var task = new QueryTask ();
+		task.type = TaskType.QUERY;
+		task.query = sparql;
+		task.cancellable = new Cancellable ();
+		task.in_thread = in_thread;
+		task.callback = sparql_query.callback;
+		task.client_id = client_id;
+
+		query_queues[priority].push_tail (task);
+
+		sched ();
+
+		yield;
+
+		if (task.error != null) {
+			throw task.error;
+		}
+	}
+
+	public static async void sparql_update (string sparql, Priority priority, string client_id) throws Error {
+		var task = new UpdateTask ();
+		task.type = TaskType.UPDATE;
+		task.query = sparql;
+		task.priority = priority;
+		task.callback = sparql_update.callback;
+		task.client_id = client_id;
+
+		update_queues[priority].push_tail (task);
+
+		sched ();
+
+		yield;
+
+		if (task.error != null) {
+			throw task.error;
+		}
+	}
+
+	public static async GenericArray<GenericArray<HashTable<string,string>>> sparql_update_blank (string sparql, Priority priority, string client_id) throws Error {
+		var task = new UpdateTask ();
+		task.type = TaskType.UPDATE_BLANK;
+		task.query = sparql;
+		task.priority = priority;
+		task.callback = sparql_update.callback;
+		task.client_id = client_id;
+
+		update_queues[priority].push_tail (task);
+
+		sched ();
+
+		yield;
+
+		if (task.error != null) {
+			throw task.error;
+		}
+
+		return task.blank_nodes;
+	}
+
+	public static async void queue_turtle_import (File file, string client_id) throws Error {
+		var task = new TurtleTask ();
+		task.type = TaskType.TURTLE;
+		task.path = file.get_path ();
+		task.callback = queue_turtle_import.callback;
+		task.client_id = client_id;
+
+		update_queues[Priority.TURTLE].push_tail (task);
+
+		sched ();
+
+		yield;
+
+		if (task.error != null) {
+			throw task.error;
+		}
+	}
+
+	public uint get_queue_size () {
+		uint result = 0;
+
+		for (int i = 0; i < Priority.N_PRIORITIES; i++) {
+			result += query_queues[i].get_length ();
+			result += update_queues[i].get_length ();
+		}
+		return result;
+	}
+
+	public static void unreg_batches (string client_id) {
+		unowned List<Task> list, cur;
+		unowned Queue<Task> queue;
+
+		for (int i = 0; i < running_tasks.length; i++) {
+			unowned QueryTask task = running_tasks[i] as QueryTask;
+			if (task != null && task.client_id == client_id && task.cancellable != null) {
+				task.cancellable.cancel ();
+			}
+		}
+
+		for (int i = 0; i < Priority.N_PRIORITIES; i++) {
+			queue = query_queues[i];
+			list = queue.head;
+			while (list != null) {
+				cur = list;
+				list = list.next;
+				unowned Task task = cur.data;
+
+				if (task != null && task.client_id == client_id) {
+					queue.delete_link (cur);
+
+					task.error = new DBusError.FAILED ("Client disappeared");
+					task.callback ();
+				}
+			}
+
+			queue = update_queues[i];
+			list = queue.head;
+			while (list != null) {
+				cur = list;
+				list = list.next;
+				unowned Task task = cur.data;
+
+				if (task != null && task.client_id == client_id) {
+					queue.delete_link (cur);
+
+					task.error = new DBusError.FAILED ("Client disappeared");
+					task.callback ();
+				}
+			}
+		}
+
+		sched ();
+	}
+
+	public static async void pause () {
+		Tracker.Store.active = false;
+
+		if (n_queries_running > 0 || update_running) {
+			active_callback = pause.callback;
+			yield;
+			active_callback = null;
+		}
+
+		if (active) {
+			sched ();
+		}
+	}
+
+	public static void resume () {
+		Tracker.Store.active = true;
+
+		sched ();
+	}
+}
diff --git a/src/tracker-store/tracker-writeback.vapi b/src/tracker-store/tracker-writeback.vapi
new file mode 100644
index 0000000..1c50eca
--- /dev/null
+++ b/src/tracker-store/tracker-writeback.vapi
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011, Nokia <ivan frade nokia com>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+namespace Tracker {
+	[CCode (has_array_length = false, array_null_terminated = true, has_target = false, cheader_filename = "tracker-store/tracker-writeback.h")]
+	public delegate string[] WritebackGetPredicatesFunc ();
+
+	[CCode (cheader_filename = "tracker-store/tracker-writeback.h")]
+	namespace Writeback {
+		public void init (WritebackGetPredicatesFunc callback);
+		public void shutdown ();
+		public void check (int graph_id, string graph, int subject_id, string subject, int pred_id, int object_id, string object, GLib.PtrArray rdf_types);
+		public unowned GLib.HashTable<int, GLib.Array<int>> get_ready ();
+		public void reset_pending ();
+		public void reset_ready ();
+		public void transact ();
+	}
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]