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



commit 4a945e69c065e65950efaa037d980bc3dba06899
Author: Jürg Billeter <j bitron ch>
Date:   Tue Dec 28 08:42:03 2010 +0100

    tracker-store: Port to GDBus/Vala

 configure.ac                                       |    4 +-
 src/libtracker-common/libtracker-common.vapi       |   21 +
 src/libtracker-common/tracker-dbus.h               |    4 +-
 src/libtracker-data/libtracker-data.vapi           |   63 ++-
 src/libtracker-data/tracker-sparql-query.vala      |   17 +-
 src/tracker-store/.gitignore                       |    9 +
 src/tracker-store/Makefile.am                      |   90 +--
 src/tracker-store/tracker-backup.c                 |  215 -----
 src/tracker-store/tracker-backup.h                 |   64 --
 src/tracker-store/tracker-backup.vala              |  110 +++
 src/tracker-store/tracker-config.vapi              |   27 +
 src/tracker-store/tracker-dbus.c                   |  382 ---------
 src/tracker-store/tracker-dbus.vala                |  252 ++++++
 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                   |  520 ------------
 src/tracker-store/tracker-main.vala                |  371 ++++++++
 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 --
 .../{tracker-dbus.h => tracker-statistics.vala}    |   37 +-
 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            |  212 +++++
 src/tracker-store/tracker-store.c                  |  691 ---------------
 src/tracker-store/tracker-store.h                  |   92 --
 src/tracker-store/tracker-store.vala               |  431 ++++++++++
 src/tracker-store/tracker-writeback.vapi           |   34 +
 35 files changed, 2124 insertions(+), 4339 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 1ff2fb1..c184a79 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,7 +46,7 @@ AC_INIT([tracker],
         [tracker],
         [http://www.tracker-project.org])
 
-AC_CONFIG_SRCDIR([src/tracker-store/tracker-main.c])
+AC_CONFIG_SRCDIR([src/tracker-store/tracker-main.vala])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_HEADERS([config.h])
 
@@ -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/libtracker-common/libtracker-common.vapi b/src/libtracker-common/libtracker-common.vapi
index eb546db..36644c8 100644
--- a/src/libtracker-common/libtracker-common.vapi
+++ b/src/libtracker-common/libtracker-common.vapi
@@ -58,5 +58,26 @@ namespace Tracker {
 		public static void save_string_list (void *object, string property, GLib.KeyFile key_file, string group, string key);
 		public static void save_directory_list (void *object, string property, GLib.KeyFile key_file, string group, string key);
 	}
+
+	[CCode (cheader_filename = "libtracker-common/tracker-common.h")]
+	namespace Log {
+		public bool init (int verbosity, out string used_filename);
+		public void shutdown ();
+	}
+
+	[Compact]
+	[CCode (ref_function = "", unref_function = "", cheader_filename = "libtracker-common/tracker-common.h")]
+	public class DBusRequest {
+		public static DBusRequest begin (string? sender, string format,...);
+		public void debug (string format,...);
+		public void end (GLib.Error? e = null);
+		[CCode (cname = "tracker_dbus_enable_client_lookup")]
+		public static void enable_client_lookup (bool enable);
+	}
+
+	[CCode (cheader_filename = "libtracker-common/tracker-common.h")]
+	public bool env_check_xdg_dirs ();
+	[CCode (cheader_filename = "libtracker-common/tracker-common.h")]
+	public void ioprio_init ();
 }
 
diff --git a/src/libtracker-common/tracker-dbus.h b/src/libtracker-common/tracker-dbus.h
index 0d9c5ca..84e001d 100644
--- a/src/libtracker-common/tracker-dbus.h
+++ b/src/libtracker-common/tracker-dbus.h
@@ -24,9 +24,11 @@
 
 #include <gio/gio.h>
 
+#if !defined (TRACKER_STORE_COMPILATION)
 #include <dbus/dbus.h>
 #include <dbus/dbus-glib-lowlevel.h>
 #include <dbus/dbus-glib.h>
+#endif
 
 G_BEGIN_DECLS
 
@@ -143,7 +145,7 @@ void                tracker_dbus_request_debug         (TrackerDBusRequest
 void                tracker_dbus_enable_client_lookup  (gboolean                    enable);
 
 
-#ifndef NO_LIBDBUS
+#if !defined(NO_LIBDBUS) && !defined (TRACKER_STORE_COMPILATION)
 /* dbus-glib convenience API */
 TrackerDBusRequest *tracker_dbus_g_request_begin       (DBusGMethodInvocation      *context,
                                                         const gchar                *format,
diff --git a/src/libtracker-data/libtracker-data.vapi b/src/libtracker-data/libtracker-data.vapi
index 27955a0..d5bba5a 100644
--- a/src/libtracker-data/libtracker-data.vapi
+++ b/src/libtracker-data/libtracker-data.vapi
@@ -85,14 +85,38 @@ namespace Tracker {
 		public abstract DBCursor start_sparql_cursor (PropertyType[] types, string[] variable_names, bool threadsafe) throws DBInterfaceError;
 	}
 
+	[CCode (cheader_filename = "libtracker-data/tracker-db-config.h")]
+	public class DBConfig : ConfigFile {
+		public DBConfig ();
+		public bool save ();
+		public int journal_chunk_size { get; set; }
+		public string journal_rotate_destination { get; set; }
+	}
+
+	[CCode (cheader_filename = "libtracker-data/tracker-db-config.h")]
+	namespace DBJournal {
+		public void set_rotating (bool do_rotating, size_t chunk_size, string? rotate_to);
+	}
+
 	[CCode (cheader_filename = "libtracker-data/tracker-class.h")]
 	public class Class : GLib.Object {
 		public string name { get; set; }
 		public string uri { get; set; }
+		public int count { get; set; }
 		[CCode (array_length = false, array_null_terminated = true)]
 		public unowned Class[] get_super_classes ();
+		public void transact_events ();
+
+		public bool has_insert_events ();
+		public bool has_delete_events ();
+		public void foreach_delete_event (EventsForeach func);
+		public void foreach_insert_event (EventsForeach func);
+		public void reset_ready_events ();
 	}
 
+	[CCode (cheader_filename = "libtracker-data/tracker-class.h")]
+	public delegate void EventsForeach (int graph_id, int subject_id, int pred_id, int object_id);
+
 	[CCode (cheader_filename = "libtracker-data/tracker-namespace.h")]
 	public class Namespace : GLib.Object {
 		public string prefix { get; set; }
@@ -134,20 +158,45 @@ namespace Tracker {
 		public unowned Property[] get_properties ();
 	}
 
-	[CCode (cheader_filename = "libtracker-data/tracker-data-query.h,libtracker-data/tracker-data-update.h")]
+	public delegate void StatementCallback (int graph_id, string? graph, int subject_id, string subject, int predicate_id, int object_id, string object, GLib.PtrArray rdf_types);
+	public delegate void CommitCallback (bool start_timer);
+
+	[CCode (cheader_filename = "libtracker-data/tracker-data-query.h,libtracker-data/tracker-data-update.h,libtracker-data/tracker-data-backup.h")]
 	namespace Data {
 		public int query_resource_id (string uri);
+		public DBResultSet query_sparql (string query) throws Sparql.Error;
+		public DBCursor query_sparql_cursor (string query) throws Sparql.Error;
 		public void begin_db_transaction ();
 		public void commit_db_transaction ();
 		public void begin_transaction () throws DBInterfaceError;
 		public void commit_transaction () throws DBInterfaceError;
 		public void rollback_transaction ();
-		public void delete_statement (string graph, string subject, string predicate, string object) throws Sparql.Error, DateError;
-		public void insert_statement (string graph, string subject, string predicate, string object) throws Sparql.Error, DateError;
-		public void insert_statement_with_uri (string graph, string subject, string predicate, string object) throws Sparql.Error;
-		public void insert_statement_with_string (string graph, string subject, string predicate, string object) throws Sparql.Error, DateError;
+		public void update_sparql (string update) throws Sparql.Error;
+		public GLib.GenericArray<GLib.GenericArray<GLib.HashTable<string,string>>> update_sparql_blank (string update) throws Sparql.Error;
+		public void load_turtle_file (GLib.File file) throws Sparql.Error;
+		public void notify_transaction (bool start_timer);
+		public void delete_statement (string? graph, string subject, string predicate, string object) throws Sparql.Error, DateError;
+		public void insert_statement (string? graph, string subject, string predicate, string object) throws Sparql.Error, DateError;
+		public void insert_statement_with_uri (string? graph, string subject, string predicate, string object) throws Sparql.Error;
+		public void insert_statement_with_string (string? graph, string subject, string predicate, string object) throws Sparql.Error, DateError;
 		public void update_buffer_flush () throws DBInterfaceError;
 		public void update_buffer_might_flush () throws DBInterfaceError;
+		public void sync ();
+
+		public void add_insert_statement_callback (StatementCallback callback);
+		public void add_delete_statement_callback (StatementCallback callback);
+		public void add_commit_statement_callback (CommitCallback callback);
+		public void add_rollback_statement_callback (CommitCallback callback);
+		public void remove_insert_statement_callback (StatementCallback callback);
+		public void remove_delete_statement_callback (StatementCallback callback);
+		public void remove_commit_statement_callback (CommitCallback callback);
+		public void remove_rollback_statement_callback (CommitCallback callback);
+
+		[CCode (cheader_filename = "libtracker-data/tracker-data-backup.h")]
+		public delegate void BackupFinished (GLib.Error error);
+
+		public void backup_save (GLib.File destination, owned BackupFinished callback);
+		public void backup_restore (GLib.File journal, owned BackupFinished callback, [CCode (array_length = false)] string[]? test_schema, BusyCallback busy_callback);
 	}
 
 	[CCode (cheader_filename = "libtracker-data/tracker-data-manager.h")]
@@ -158,5 +207,7 @@ namespace Tracker {
 
 	[CCode (cheader_filename = "libtracker-data/tracker-db-interface-sqlite.h")]
 	public const string COLLATION_NAME;
-}
 
+	[CCode (cheader_filename = "libtracker-data/tracker-db-dbus.h")]
+	public string[] dbus_query_result_to_strv (DBResultSet result_set, int column);
+}
diff --git a/src/libtracker-data/tracker-sparql-query.vala b/src/libtracker-data/tracker-sparql-query.vala
index 1fafb99..4ccb793 100644
--- a/src/libtracker-data/tracker-sparql-query.vala
+++ b/src/libtracker-data/tracker-sparql-query.vala
@@ -437,7 +437,7 @@ public class Tracker.Sparql.Query : Object {
 		}
 	}
 
-	public PtrArray? execute_update (bool blank) throws DBInterfaceError, Sparql.Error, DateError {
+	public GenericArray<GenericArray<HashTable<string,string>>>? execute_update (bool blank) throws DBInterfaceError, Sparql.Error, DateError {
 		assert (update_extensions);
 
 		scanner = new SparqlScanner ((char*) query_string, (long) query_string.length);
@@ -456,9 +456,9 @@ public class Tracker.Sparql.Query : Object {
 
 		parse_prologue ();
 
-		PtrArray blank_nodes = null;
+		GenericArray<GenericArray<HashTable<string,string>>> blank_nodes = null;
 		if (blank) {
-			blank_nodes = new PtrArray ();
+			blank_nodes = new GenericArray<GenericArray<HashTable<string,string>>> ();
 		}
 
 		// SPARQL update supports multiple operations in a single query
@@ -468,7 +468,7 @@ public class Tracker.Sparql.Query : Object {
 			case SparqlTokenType.WITH:
 			case SparqlTokenType.INSERT:
 			case SparqlTokenType.DELETE:
-				PtrArray* ptr = execute_insert_or_delete (blank);
+				GenericArray<HashTable<string,string>> ptr = execute_insert_or_delete (blank);
 				if (ptr != null) {
 					blank_nodes.add (ptr);
 				}
@@ -604,7 +604,7 @@ public class Tracker.Sparql.Query : Object {
 		}
 	}
 
-	PtrArray? execute_insert_or_delete (bool blank) throws DBInterfaceError, Sparql.Error, DateError {
+	GenericArray<HashTable<string,string>>? execute_insert_or_delete (bool blank) throws DBInterfaceError, Sparql.Error, DateError {
 		// INSERT or DELETE
 
 		if (accept (SparqlTokenType.WITH)) {
@@ -697,10 +697,10 @@ public class Tracker.Sparql.Query : Object {
 
 		this.delete_statements = delete_statements;
 
-		PtrArray update_blank_nodes = null;
+		GenericArray<HashTable<string,string>> update_blank_nodes = null;
 
 		if (blank) {
-			update_blank_nodes = new PtrArray ();
+			update_blank_nodes = new GenericArray<HashTable<string,string>> ();
 		}
 
 		// iterate over all solutions
@@ -726,8 +726,7 @@ public class Tracker.Sparql.Query : Object {
 				parse_construct_triples_block (var_value_map);
 
 				if (blank) {
-					HashTable<string,string>* ptr = (owned) blank_nodes;
-					update_blank_nodes.add (ptr);
+					update_blank_nodes.add ((owned) blank_nodes);
 				}
 
 				Data.update_buffer_might_flush ();
diff --git a/src/tracker-store/.gitignore b/src/tracker-store/.gitignore
index 5b17e28..af38b38 100644
--- a/src/tracker-store/.gitignore
+++ b/src/tracker-store/.gitignore
@@ -1 +1,10 @@
 tracker-store
+tracker-backup.c
+tracker-dbus.c
+tracker-main.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..1804b4d 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)                        \
+	-DTRACKER_STORE_COMPILATION                    \
+	-DGETTEXT_PACKAGE=\"NULL\"
 
 #
 # Daemon sources
@@ -16,31 +18,39 @@ 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-locale-change.c                        \
+	tracker-main.vala                              \
+	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-writeback.c                            \
-	tracker-writeback.h                            \
-	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-locale-change.h                        \
-	tracker-locale-change.c
+	tracker-writeback.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 +59,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..a9a1bff
--- /dev/null
+++ b/src/tracker-store/tracker-backup.vala
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2006, Jamie McCracken <jamiemcc gnome org>
+ * Copyright (C) 2008, 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 {
+		var request = DBusRequest.begin (sender, "D-Bus request to save backup into '%s'", destination_uri);
+		try {
+			var destination = File.new_for_uri (destination_uri);
+
+			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 ();
+			}
+
+			yield Tracker.Store.pause ();
+
+			Error backup_error = null;
+			Data.backup_save (destination, error => {
+				backup_error = error;
+				save.callback ();
+			});
+			yield;
+
+			if (resources != null) {
+				Tracker.Events.init (getter);
+				resources.enable_signals ();
+			}
+
+			if (backup_error != null) {
+				throw backup_error;
+			}
+
+			Tracker.Store.resume ();
+
+			request.end ();
+		} catch (Error e) {
+			request.end (e);
+			throw e;
+		}
+	}
+
+	public async void restore (BusName sender, string journal_uri) throws Error {
+		var request = DBusRequest.begin (sender, "D-Bus request to restore backup from '%s'", journal_uri);
+		try {
+			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 ();
+			}
+
+			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 ();
+
+			Error restore_error = null;
+			Data.backup_restore (journal, error => {
+				restore_error = error;
+				restore.callback ();
+			}, null, busy_callback);
+			yield;
+
+			if (resources != null) {
+				Tracker.Events.init (getter);
+				resources.enable_signals ();
+			}
+
+			if (restore_error != null) {
+				throw restore_error;
+			}
+
+			Tracker.Store.resume ();
+
+			request.end ();
+		} catch (Error e) {
+			request.end (e);
+			throw e;
+		}
+	}
+}
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..75b54a0
--- /dev/null
+++ b/src/tracker-store/tracker-dbus.vala
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2006, Jamie McCracken <jamiemcc gnome org>
+ * Copyright (C) 2008, 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 ());
+
+		return lconnection.register_object (path, object);
+	}
+
+	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.vala b/src/tracker-store/tracker-main.vala
new file mode 100644
index 0000000..5583438
--- /dev/null
+++ b/src/tracker-store/tracker-main.vala
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2006, Jamie McCracken <jamiemcc gnome org>
+ * Copyright (C) 2008, 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.
+ */
+
+class Tracker.Main {
+	[CCode (cname = "PACKAGE_VERSION")]
+	extern const string PACKAGE_VERSION;
+
+	const string LICENSE = "This program is free software and comes without any warranty.
+It is licensed under version 2 or later of the General Public
+License which can be viewed at:
+  http://www.gnu.org/licenses/gpl.txt
+";
+
+	const int SELECT_CACHE_SIZE = 100;
+	const int UPDATE_CACHE_SIZE = 100;
+
+	static MainLoop main_loop;
+	static string log_filename;
+
+	static bool shutdown;
+
+/* Private command line parameters */
+	static bool version;
+	static int verbosity = -1;
+	static bool force_reindex;
+	static bool readonly_mode;
+
+	const OptionEntry entries[] = {
+		/* Daemon options */
+		{ "version", 'V', 0, OptionArg.NONE, ref version, N_("Displays version information"), null },
+		{ "verbosity", 'v', 0, OptionArg.INT, ref verbosity, N_("Logging, 0 = errors only, 1 = minimal, 2 = detailed and 3 = debug (default = 0)"), null },
+
+		/* Indexer options */
+		{ "force-reindex", 'r', 0, OptionArg.NONE, ref force_reindex, N_("Force a re-index of all content"), null },
+		{ "readonly-mode", 'n', 0, OptionArg.NONE, ref readonly_mode, N_("Only allow read based actions on the database"), null },
+		{ null }
+	};
+
+	static void sanity_check_option_values (Tracker.Config config) {
+		message ("General options:");
+		message ("  Verbosity  ............................  %d", config.verbosity);
+
+		message ("Store options:");
+		message ("  Readonly mode  ........................  %s", readonly_mode ? "yes" : "no");
+	}
+
+	static void do_shutdown () {
+		if (main_loop != null) {
+			main_loop.quit ();
+		}
+
+		shutdown = true;
+	}
+
+	static bool shutdown_timeout_cb () {
+		critical ("Could not exit in a timely fashion - terminating...");
+		Process.exit (1);
+	}
+
+	static bool in_loop = false;
+
+	static void signal_handler (int signo) {
+		/* Die if we get re-entrant signals handler calls */
+		if (in_loop) {
+			Process.exit (1);
+		}
+
+		switch (signo) {
+		case Posix.SIGTERM:
+		case Posix.SIGINT:
+			in_loop = true;
+			do_shutdown ();
+
+			if (strsignal (signo) != null) {
+				print ("\n");
+				print ("Received signal:%d->'%s'", signo, strsignal (signo));
+			}
+			break;
+		default:
+			if (strsignal (signo) != null) {
+				print ("\n");
+				print ("Received signal:%d->'%s'", signo, strsignal (signo));
+			}
+			break;
+		}
+	}
+
+	static void initialize_signal_handler () {
+		var empty_mask = Posix.sigset_t ();
+		Posix.sigemptyset (empty_mask);
+
+		var act = Posix.sigaction_t ();
+		act.sa_handler = signal_handler;
+		act.sa_mask = empty_mask;
+		act.sa_flags = 0;
+
+		Posix.sigaction (Posix.SIGTERM, act, null);
+		Posix.sigaction (Posix.SIGINT, act, null);
+		Posix.sigaction (Posix.SIGHUP, act, null);
+	}
+
+	static void initialize_priority () {
+		/* Set disk IO priority and scheduling */
+		Tracker.ioprio_init ();
+
+		/* NOTE: We only set the nice() value when crawling, for all
+		 * other times we don't have a nice() value. Check the
+		 * tracker-status code to see where this is done.
+		 */
+	}
+
+	[CCode (array_length = false, array_null_terminated = true)]
+	static string[] get_notifiable_classes () {
+		string[] classes_to_signal = null;
+
+		try {
+			var result_set = Tracker.Data.query_sparql ("SELECT ?class WHERE { ?class tracker:notify true }");
+
+			if (result_set != null) {
+				classes_to_signal = Tracker.dbus_query_result_to_strv (result_set, 0);
+			}
+		} catch (Error e) {
+			critical ("Unable to retrieve tracker:notify classes: %s", e.message);
+		}
+
+		return classes_to_signal;
+	}
+
+	[CCode (array_length = false, array_null_terminated = true)]
+	static string[] get_writeback_predicates () {
+		string[] predicates_to_signal = null;
+
+		try {
+			var result_set = Tracker.Data.query_sparql ("SELECT ?predicate WHERE { ?predicate tracker:writeback true }");
+
+			if (result_set != null) {
+				predicates_to_signal = Tracker.dbus_query_result_to_strv (result_set, 0);
+			}
+		} catch (Error e) {
+			critical ("Unable to retrieve tracker:writeback properties: %s", e.message);
+		}
+
+		return predicates_to_signal;
+	}
+
+	static void config_verbosity_changed_cb (Object object, ParamSpec? spec) {
+		int verbosity = ((Tracker.Config) object).verbosity;
+
+		message ("Log verbosity is set to %d, %s D-Bus client lookup",
+		         verbosity,
+		         verbosity > 0 ? "enabling" : "disabling");
+
+		Tracker.DBusRequest.enable_client_lookup (verbosity > 0);
+	}
+
+	static int main (string args[]) {
+		Intl.setlocale (LocaleCategory.ALL, "");
+
+		Intl.bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+		Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+		Intl.textdomain (GETTEXT_PACKAGE);
+
+		/* Set timezone info */
+		tzset ();
+
+		try {
+			/* Translators: this messagge will apper immediately after the
+			 * usage string - Usage: COMMAND <THIS_MESSAGE>
+			 */
+			var context = new OptionContext (_("- start the tracker daemon"));
+			context.add_main_entries (entries, null);
+			context.parse (ref args);
+		} catch (Error e) {
+			printerr ("Invalid arguments, %s\n", e.message);
+			return 1;
+		}
+
+		if (version) {
+			/* Print information */
+			print ("\nTracker " + PACKAGE_VERSION + "\n\n" + LICENSE + "\n");
+			return 0;
+		}
+
+		print ("Initializing tracker-store...\n");
+
+		initialize_signal_handler ();
+
+		/* Check XDG spec locations XDG_DATA_HOME _MUST_ be writable. */
+		if (!Tracker.env_check_xdg_dirs ()) {
+			return 1;
+		}
+
+		/* This makes sure we don't steal all the system's resources */
+		initialize_priority ();
+
+		/* Initialize major subsystems */
+		var config = new Tracker.Config ();
+		var db_config = new Tracker.DBConfig ();
+
+		ulong config_verbosity_id = config.notify["verbosity"].connect (config_verbosity_changed_cb);
+
+		/* Daemon command line arguments */
+		if (verbosity > -1) {
+			config.verbosity = verbosity;
+		} else {
+			/* Make sure we enable/disable the dbus client lookup */
+			config_verbosity_changed_cb (config, null);
+		}
+
+		if (!Tracker.DBus.init ()) {
+			return 1;
+		}
+
+		/* Initialize other subsystems */
+		Tracker.Log.init (3 /*tracker_config_get_verbosity (config)*/, out log_filename);
+		print ("Starting log:\n  File:'%s'\n", log_filename);
+
+		sanity_check_option_values (config);
+
+		DBManagerFlags flags = DBManagerFlags.REMOVE_CACHE;
+
+		if (force_reindex) {
+			/* TODO port backup support
+			   backup_user_metadata (config, language); */
+
+			flags |= DBManagerFlags.FORCE_REINDEX;
+		}
+
+		var notifier = Tracker.DBus.register_notifier ();
+		var busy_callback = notifier.get_callback ();
+
+		Tracker.Store.init ();
+
+		/* Make Tracker available for introspection */
+		if (!Tracker.DBus.register_objects ()) {
+			return 1;
+		}
+
+		if (!Tracker.DBus.register_names ()) {
+			return 1;
+		}
+
+		int chunk_size_mb = db_config.journal_chunk_size;
+		size_t chunk_size = (size_t) ((size_t) chunk_size_mb * (size_t) 1024 * (size_t) 1024);
+		string rotate_to = db_config.journal_rotate_destination;
+
+		if (rotate_to == "") {
+			rotate_to = null;
+		}
+
+		bool do_rotating = (chunk_size_mb != -1);
+
+		Tracker.DBJournal.set_rotating (do_rotating, chunk_size, rotate_to);
+
+		int select_cache_size, update_cache_size;
+		string cache_size_s;
+
+		cache_size_s = Environment.get_variable ("TRACKER_STORE_SELECT_CACHE_SIZE");
+		if (cache_size_s != null && cache_size_s != "") {
+			select_cache_size = cache_size_s.to_int ();
+		} else {
+			select_cache_size = SELECT_CACHE_SIZE;
+		}
+
+		cache_size_s = Environment.get_variable ("TRACKER_STORE_UPDATE_CACHE_SIZE");
+		if (cache_size_s != null && cache_size_s != "") {
+			update_cache_size = cache_size_s.to_int ();
+		} else {
+			update_cache_size = UPDATE_CACHE_SIZE;
+		}
+
+		bool is_first_time_index;
+
+		if (!Tracker.Data.Manager.init (flags,
+			                        null,
+			                        out is_first_time_index,
+			                        true,
+			                        select_cache_size,
+			                        update_cache_size,
+			                        busy_callback,
+			                        "Initializing")) {
+
+			return 1;
+		}
+
+
+		db_config = null;
+		notifier = null;
+
+		if (!shutdown) {
+			/* Setup subscription to get notified of locale changes */
+			Tracker.locale_change_initialize_subscription ();
+
+			Tracker.DBus.register_prepare_class_signal ();
+
+			Tracker.Events.init (get_notifiable_classes);
+			Tracker.Writeback.init (get_writeback_predicates);
+			Tracker.Store.resume ();
+
+			message ("Waiting for D-Bus requests...");
+		}
+
+		/* Set our status as running, if this is FALSE, threads stop
+		 * doing what they do and shutdown.
+		 */
+		if (!shutdown) {
+			main_loop = new MainLoop ();
+			main_loop.run ();
+		}
+
+		/*
+		 * Shutdown the daemon
+		 */
+		message ("Shutdown started");
+
+		Tracker.Store.shutdown ();
+
+		Timeout.add (5000, shutdown_timeout_cb, Priority.LOW);
+
+		message ("Cleaning up");
+
+		/* Shutdown major subsystems */
+		Tracker.Writeback.shutdown ();
+		Tracker.Events.shutdown ();
+
+		Tracker.locale_change_shutdown_subscription ();
+
+		Tracker.DBus.shutdown ();
+		Tracker.Data.Manager.shutdown ();
+		Tracker.Log.shutdown ();
+
+		config.disconnect (config_verbosity_id);
+		config = null;
+
+		/* This will free rotate_to up in the journal code */
+		Tracker.DBJournal.set_rotating ((chunk_size_mb != -1), chunk_size, null);
+
+		print ("\nOK\n\n");
+
+		log_filename = null;
+
+		main_loop = null;
+
+		return 0;
+	}
+
+	[CCode (cname = "GETTEXT_PACKAGE")]
+	extern const string GETTEXT_PACKAGE;
+	[CCode (cname = "LOCALEDIR")]
+	extern const string LOCALEDIR;
+
+	[CCode (cname = "tzset", cheader_filename = "time.h")]
+	extern static void tzset ();
+}
diff --git a/src/tracker-store/tracker-resources.vala b/src/tracker-store/tracker-resources.vala
new file mode 100644
index 0000000..fb8a900
--- /dev/null
+++ b/src/tracker-store/tracker-resources.vala
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2006, Jamie McCracken <jamiemcc gnome org>
+ * Copyright (C) 2008, 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-dbus.h b/src/tracker-store/tracker-statistics.vala
similarity index 55%
rename from src/tracker-store/tracker-dbus.h
rename to src/tracker-store/tracker-statistics.vala
index 541b9e3..1e52917 100644
--- a/src/tracker-store/tracker-dbus.h
+++ b/src/tracker-store/tracker-statistics.vala
@@ -18,25 +18,30 @@
  * Boston, MA  02110-1301, USA.
  */
 
-#ifndef __TRACKER_STORE_DBUS_H__
-#define __TRACKER_STORE_DBUS_H__
+[DBus (name = "org.freedesktop.Tracker1.Statistics")]
+public class Tracker.Statistics : Object {
+	public const string PATH = "/org/freedesktop/Tracker1/Statistics";
 
-#include <glib.h>
+	[DBus (signature = "aas")]
+	public new Variant get (BusName sender) {
+		var request = DBusRequest.begin (sender, "Statistics.Get");
 
-#include <dbus/dbus-glib-bindings.h>
+		var builder = new VariantBuilder ((VariantType) "aas");
 
-#include "tracker-status.h"
+		foreach (var cl in Ontologies.get_classes ()) {
+			if (cl.count == 0) {
+				/* skip classes without resources */
+				continue;
+			}
 
-G_BEGIN_DECLS
+			builder.open ((VariantType) "as");
+			builder.add ("s", cl.name);
+			builder.add ("s", cl.count.to_string ());
+			builder.close ();
+		}
 
-gboolean        tracker_dbus_init                          (void);
-void            tracker_dbus_shutdown                      (void);
-gboolean        tracker_dbus_register_objects              (void);
-GObject        *tracker_dbus_get_object                    (GType type);
-TrackerStatus  *tracker_dbus_register_notifier             (void);
-gboolean        tracker_dbus_register_names                (void);
-gboolean        tracker_dbus_register_prepare_class_signal (void);
+		request.end ();
 
-G_END_DECLS
-
-#endif /* __TRACKER_STORE_DBUS_H__ */
+		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..7d861d7
--- /dev/null
+++ b/src/tracker-store/tracker-status.vala
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * 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..cbb320d
--- /dev/null
+++ b/src/tracker-store/tracker-steroids.vala
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2010, Codeminded BVBA <abustany gnome org>
+ *
+ * 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..747b487
--- /dev/null
+++ b/src/tracker-store/tracker-store.vala
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2009, 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 () {
+		/*if ((tmp = g_getenv("TRACKER_STORE_MAX_TASK_TIME")) != NULL) {
+			max_task_time = atoi (tmp);
+		} 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) {
+			// ignore harmless thread creation error
+			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]