[gnome-documents/wip/skydrive: 1/28] miner: add a skeleton SkyDrive miner based on libzapojit



commit 8eee2281fc085e93a99d6b718e60268466873aff
Author: Debarshi Ray <debarshir gnome org>
Date:   Tue May 22 17:30:45 2012 +0200

    miner: add a skeleton SkyDrive miner based on libzapojit
    
    Fixes: https://bugzilla.gnome.org/666535

 configure.ac               |    1 +
 src/Makefile-miner.am      |   22 ++
 src/miner/gd-zpj-miner.c   |  709 ++++++++++++++++++++++++++++++++++++++++++++
 src/miner/gd-zpj-miner.h   |   80 +++++
 src/miner/zpj-miner-main.c |  247 +++++++++++++++
 5 files changed, 1059 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index d676476..fb2b031 100644
--- a/configure.ac
+++ b/configure.ac
@@ -78,6 +78,7 @@ PKG_CHECK_MODULES(MINER,
                   gio-2.0 >= $GLIB_MIN_VERSION
                   goa-1.0 >= $GOA_MIN_VERSION
                   libgdata >= $GDATA_MIN_VERSION
+                  zapojit-0.0
                   oauth)
 
 GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
diff --git a/src/Makefile-miner.am b/src/Makefile-miner.am
index b70efea..d880443 100644
--- a/src/Makefile-miner.am
+++ b/src/Makefile-miner.am
@@ -19,3 +19,25 @@ gd_tracker_gdata_miner_LDADD = \
     $(MINER_LIBS) \
     $(DOCUMENTS_LIBS) \
     $(NULL)
+
+libexec_PROGRAMS += gd-tracker-zpj-miner
+
+gd_tracker_zpj_miner_SOURCES = \
+    miner/zpj-miner-main.c \
+    miner/gd-zpj-miner.c \
+    miner/gd-zpj-miner.h \
+    $(NULL)
+
+gd_tracker_zpj_miner_CFLAGS = \
+    -DG_DISABLE_DEPRECATED \
+    -DGOA_API_IS_SUBJECT_TO_CHANGE \
+    -I$(top_srcdir)/src/lib \
+    $(MINER_CFLAGS) \
+    $(DOCUMENTS_CFLAGS) \
+    $(NULL)
+
+gd_tracker_zpj_miner_LDADD = \
+    libgdprivate-1.0.la  \
+    $(MINER_LIBS) \
+    $(DOCUMENTS_LIBS) \
+    $(NULL)
diff --git a/src/miner/gd-zpj-miner.c b/src/miner/gd-zpj-miner.c
new file mode 100644
index 0000000..28f995f
--- /dev/null
+++ b/src/miner/gd-zpj-miner.c
@@ -0,0 +1,709 @@
+/*
+ * Copyright (c) 2012 Red Hat, Inc.
+ *
+ * Gnome Documents 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.
+ *
+ * Gnome Documents 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 Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#include <goa/goa.h>
+#include <zpj/zpj.h>
+#include <unistd.h>
+
+#include "gd-zpj-miner.h"
+#include "gd-utils.h"
+
+#define MINER_IDENTIFIER "gd:zpj:miner:30058620-777c-47a3-a19c-a6cdf4a315c4"
+
+G_DEFINE_TYPE (GdZpjMiner, gd_zpj_miner, G_TYPE_OBJECT)
+
+struct _GdZpjMinerPrivate {
+  GoaClient *client;
+  TrackerSparqlConnection *connection;
+
+  GCancellable *cancellable;
+  GSimpleAsyncResult *result;
+
+  GList *pending_jobs;
+};
+
+typedef struct {
+  GdZpjMiner *self;
+  TrackerSparqlConnection *connection; /* borrowed from GdZpjMiner */
+  gulong miner_cancellable_id;
+
+  GoaAccount *account;
+  ZpjSkydrive *service;
+  GSimpleAsyncResult *async_result;
+  GCancellable *cancellable;
+
+  GHashTable *previous_resources;
+} AccountMinerJob;
+
+static void
+miner_cancellable_cancelled_cb (GCancellable *cancellable,
+                                gpointer user_data)
+{
+  AccountMinerJob *job = user_data;
+
+  /* forward the cancel signal to the ongoing job */
+  g_cancellable_cancel (job->cancellable);
+}
+
+static void
+account_miner_job_free (AccountMinerJob *job)
+{
+  if (job->miner_cancellable_id != 0)
+    g_cancellable_disconnect (job->self->priv->cancellable,
+                              job->miner_cancellable_id);
+
+  g_clear_object (&job->service);
+  g_clear_object (&job->self);
+  g_clear_object (&job->account);
+  g_clear_object (&job->async_result);
+
+  g_hash_table_unref (job->previous_resources);
+
+  g_slice_free (AccountMinerJob, job);
+}
+
+static AccountMinerJob *
+account_miner_job_new (GdZpjMiner *self,
+                       GoaObject *object)
+{
+  AccountMinerJob *retval;
+  ZpjGoaAuthorizer *authorizer;
+  GoaAccount *account;
+
+  account = goa_object_get_account (object);
+  g_assert (account != NULL);
+
+  retval = g_slice_new0 (AccountMinerJob);
+  retval->self = g_object_ref (self);
+  retval->cancellable = g_cancellable_new ();
+  retval->account = account;
+  retval->connection = self->priv->connection;
+  retval->previous_resources =
+    g_hash_table_new_full (g_str_hash, g_str_equal,
+                           (GDestroyNotify) g_free, (GDestroyNotify) g_free);
+
+  if (self->priv->cancellable != NULL)
+      retval->miner_cancellable_id =
+        g_cancellable_connect (self->priv->cancellable,
+                               G_CALLBACK (miner_cancellable_cancelled_cb),
+                               retval, NULL);
+
+  authorizer = zpj_goa_authorizer_new (object);
+  retval->service = zpj_skydrive_new (ZPJ_AUTHORIZER (authorizer));
+
+  /* the service takes ownership of the authorizer */
+  g_object_unref (authorizer);
+
+  return retval;
+}
+
+static void
+previous_resources_cleanup_foreach (gpointer key,
+                                    gpointer value,
+                                    gpointer user_data)
+{
+  const gchar *resource = value;
+  GString *delete = user_data;
+
+  g_string_append_printf (delete, "<%s> a rdfs:Resource . ", resource);
+}
+
+static void
+account_miner_job_cleanup_previous (AccountMinerJob *job,
+                                    GError **error)
+{
+  GString *delete;
+
+  delete = g_string_new (NULL);
+  g_string_append (delete, "DELETE { ");
+
+  /* the resources left here are those who were in the database,
+   * but were not found during the query; remove them from the database.
+   */
+  g_hash_table_foreach (job->previous_resources,
+                        previous_resources_cleanup_foreach,
+                        delete);
+
+  g_string_append (delete, "}");
+
+  tracker_sparql_connection_update (job->connection,
+                                    delete->str,
+                                    G_PRIORITY_DEFAULT,
+                                    job->cancellable,
+                                    error);
+
+  g_string_free (delete, TRUE);
+}
+
+static void
+account_miner_job_query_zpj (AccountMinerJob *job,
+                             GError **error)
+{
+}
+
+
+static void
+account_miner_job_query_existing (AccountMinerJob *job,
+                                  GError **error)
+{
+  GString *select;
+  TrackerSparqlCursor *cursor;
+
+  select = g_string_new (NULL);
+  g_string_append_printf (select,
+                          "SELECT ?urn nao:identifier(?urn) WHERE { ?urn nie:dataSource <gd:goa-account:%s> }",
+                          goa_account_get_id (job->account));
+
+  cursor = tracker_sparql_connection_query (job->connection,
+                                            select->str,
+                                            job->cancellable,
+                                            error);
+  g_string_free (select, TRUE);
+
+  if (cursor == NULL)
+    return;
+
+  while (tracker_sparql_cursor_next (cursor, job->cancellable, error))
+    {
+      g_hash_table_insert (job->previous_resources,
+                           g_strdup (tracker_sparql_cursor_get_string (cursor, 1, NULL)),
+                           g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL)));
+    }
+
+  g_object_unref (cursor);
+}
+
+static void
+account_miner_job_ensure_datasource (AccountMinerJob *job,
+                                     GError **error)
+{
+  GString *datasource_insert;
+
+  datasource_insert = g_string_new (NULL);
+  g_string_append_printf (datasource_insert,
+                          "INSERT OR REPLACE { <gd:goa-account:%s> a nie:DataSource ; nao:identifier \"%s\" }",
+                          goa_account_get_id (job->account), MINER_IDENTIFIER);
+
+  tracker_sparql_connection_update (job->connection,
+                                    datasource_insert->str,
+                                    G_PRIORITY_DEFAULT,
+                                    job->cancellable,
+                                    error);
+
+  g_string_free (datasource_insert, TRUE);
+}
+
+static gboolean
+account_miner_job (GIOSchedulerJob *sched_job,
+                   GCancellable *cancellable,
+                   gpointer user_data)
+{
+  AccountMinerJob *job = user_data;
+  GError *error = NULL;
+
+  account_miner_job_ensure_datasource (job, &error);
+
+  if (error != NULL)
+    goto out;
+
+  account_miner_job_query_existing (job, &error);
+
+  if (error != NULL)
+    goto out;
+
+  account_miner_job_query_zpj (job, &error);
+
+  if (error != NULL)
+    goto out;
+
+  account_miner_job_cleanup_previous (job, &error);
+
+  if (error != NULL)
+    goto out;
+
+ out:
+  if (error != NULL)
+    g_simple_async_result_take_error (job->async_result, error);
+
+  g_simple_async_result_complete_in_idle (job->async_result);
+
+  return FALSE;
+}
+
+static void
+account_miner_job_process_async (AccountMinerJob *job,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+  g_assert (job->async_result == NULL);
+
+  job->async_result = g_simple_async_result_new (NULL, callback, user_data,
+                                                 account_miner_job_process_async);
+  g_simple_async_result_set_op_res_gpointer (job->async_result, job, NULL);
+
+  g_io_scheduler_push_job (account_miner_job, job, NULL,
+                           G_PRIORITY_DEFAULT,
+                           job->cancellable);
+}
+
+static gboolean
+account_miner_job_process_finish (GAsyncResult *res,
+                                  GError **error)
+{
+  GSimpleAsyncResult *simple_res = G_SIMPLE_ASYNC_RESULT (res);
+
+  g_assert (g_simple_async_result_is_valid (res, NULL,
+                                            account_miner_job_process_async));
+
+  if (g_simple_async_result_propagate_error (simple_res, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+gd_zpj_miner_complete_error (GdZpjMiner *self,
+                               GError *error)
+{
+  g_assert (self->priv->result != NULL);
+
+  g_simple_async_result_take_error (self->priv->result, error);
+  g_simple_async_result_complete_in_idle (self->priv->result);
+}
+
+static void
+gd_zpj_miner_check_pending_jobs (GdZpjMiner *self)
+{
+  if (g_list_length (self->priv->pending_jobs) == 0)
+    g_simple_async_result_complete_in_idle (self->priv->result);
+}
+
+static void
+miner_job_process_ready_cb (GObject *source,
+                            GAsyncResult *res,
+                            gpointer user_data)
+{
+  AccountMinerJob *job = user_data;
+  GdZpjMiner *self = job->self;
+  GError *error = NULL;
+
+  account_miner_job_process_finish (res, &error);
+
+  if (error != NULL)
+    {
+      g_printerr ("Error while refreshing account %s: %s",
+                  goa_account_get_id (job->account), error->message);
+
+      g_error_free (error);
+    }
+
+  self->priv->pending_jobs = g_list_remove (self->priv->pending_jobs,
+                                            job);
+  account_miner_job_free (job);
+
+  gd_zpj_miner_check_pending_jobs (self);
+}
+
+static void
+gd_zpj_miner_setup_account (GdZpjMiner *self,
+                              GoaObject *object)
+{
+  AccountMinerJob *job;
+
+  job = account_miner_job_new (self, object);
+  self->priv->pending_jobs = g_list_prepend (self->priv->pending_jobs, job);
+
+  account_miner_job_process_async (job, miner_job_process_ready_cb, job);
+}
+
+typedef struct {
+  GdZpjMiner *self;
+  GList *doc_objects;
+  GList *acc_objects;
+  GList *old_datasources;
+} CleanupJob;
+
+static gboolean
+cleanup_old_accounts_done (gpointer data)
+{
+  CleanupJob *job = data;
+  GList *l;
+  GoaObject *object;
+  GdZpjMiner *self = job->self;
+
+  /* now setup all the current accounts */
+  for (l = job->doc_objects; l != NULL; l = l->next)
+    {
+      object = l->data;
+      gd_zpj_miner_setup_account (self, object);
+
+      g_object_unref (object);
+    }
+
+  if (job->doc_objects != NULL)
+    {
+      g_list_free (job->doc_objects);
+      job->doc_objects = NULL;
+    }
+
+  if (job->acc_objects != NULL)
+    {
+      g_list_free_full (job->acc_objects, g_object_unref);
+      job->acc_objects = NULL;
+    }
+
+  if (job->old_datasources != NULL)
+    {
+      g_list_free_full (job->old_datasources, g_free);
+      job->old_datasources = NULL;
+    }
+
+  gd_zpj_miner_check_pending_jobs (self);
+
+  g_clear_object (&job->self);
+  g_slice_free (CleanupJob, job);
+
+  return FALSE;
+}
+
+static void
+cleanup_job_do_cleanup (CleanupJob *job)
+{
+  GdZpjMiner *self = job->self;
+  GString *select, *update;
+  gboolean append_union = FALSE;
+  GList *l;
+  TrackerSparqlCursor *cursor;
+  GError *error = NULL;
+  const gchar *resource;
+
+  if (job->old_datasources == NULL)
+    return;
+
+  update = g_string_new (NULL);
+  g_string_append (update, "DELETE { ");
+
+  /* select all documents from the datasources we want to remove */
+  select = g_string_new (NULL);
+  g_string_append (select, "SELECT ?urn WHERE { ");
+
+  for (l = job->old_datasources; l != NULL; l = l->next)
+    {
+      resource = l->data;
+      g_debug ("Cleaning up old datasource %s", resource);
+
+      if (append_union)
+        g_string_append (select, " UNION ");
+      else
+        append_union = TRUE;
+
+      g_string_append_printf (select, "{ ?urn nie:dataSource \"%s\" }", resource);
+
+      /* also append the datasource itself to the list of resources to delete */
+      g_string_append_printf (update, "<%s> a rdfs:Resource . ", resource);
+    }
+
+  g_string_append (select, " }");
+
+  cursor = tracker_sparql_connection_query (self->priv->connection,
+                                            select->str,
+                                            self->priv->cancellable,
+                                            &error);
+
+  g_string_free (select, TRUE);
+
+  if (error != NULL)
+    {
+      g_printerr ("Error while cleaning up old accounts: %s\n", error->message);
+      return;
+    }
+
+  /* gather all the documents we want to remove */
+  while (tracker_sparql_cursor_next (cursor, self->priv->cancellable, NULL))
+    {
+      resource = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+      g_debug ("Cleaning up resource %s belonging to an old datasource", resource);
+
+      if (resource != NULL)
+        g_string_append_printf (update, "<%s> a rdfs:Resource . ", resource);
+    }
+
+  g_string_append (update, " }");
+  g_object_unref (cursor);
+
+  /* actually remove everything we have to remove */
+  tracker_sparql_connection_update (self->priv->connection,
+                                    update->str,
+                                    G_PRIORITY_DEFAULT,
+                                    self->priv->cancellable,
+                                    &error);
+
+  g_string_free (update, TRUE);
+
+  if (error != NULL)
+    {
+      g_printerr ("Error while cleaning up old accounts: %s\n", error->message);
+      return;
+    }
+}
+
+static gint
+cleanup_datasource_compare (gconstpointer a,
+                            gconstpointer b)
+{
+  GoaObject *object = GOA_OBJECT (a);
+  const gchar *datasource = b;
+  gint res;
+
+  GoaAccount *account;
+  gchar *object_datasource;
+
+  account = goa_object_peek_account (object);
+  g_assert (account != NULL);
+
+  object_datasource = g_strdup_printf ("gd:goa-account:%s", goa_account_get_id (account));
+  res = g_strcmp0 (datasource, object_datasource);
+
+  g_free (object_datasource);
+
+  return res;
+}
+
+static gboolean
+cleanup_job (GIOSchedulerJob *sched_job,
+             GCancellable *cancellable,
+             gpointer user_data)
+{
+  GString *select;
+  GError *error = NULL;
+  TrackerSparqlCursor *cursor;
+  const gchar *datasource;
+  GList *element;
+  CleanupJob *job = user_data;
+  GdZpjMiner *self = job->self;
+
+  /* find all our datasources in the tracker DB */
+  select = g_string_new (NULL);
+  g_string_append_printf (select, "SELECT ?datasource WHERE { ?datasource a nie:DataSource . "
+                          "?datasource nao:identifier \"%s\" }", MINER_IDENTIFIER);
+
+  cursor = tracker_sparql_connection_query (self->priv->connection,
+                                            select->str,
+                                            self->priv->cancellable,
+                                            &error);
+  g_string_free (select, TRUE);
+
+  if (error != NULL)
+    {
+      g_printerr ("Error while cleaning up old accounts: %s\n", error->message);
+      goto out;
+    }
+
+  while (tracker_sparql_cursor_next (cursor, self->priv->cancellable, NULL))
+    {
+      /* If the source we found is not in the current list, add
+       * it to the cleanup list.
+       * Note that the objects here in the list might *not* support
+       * documents, in case the switch has been disabled in System Settings.
+       * In fact, we only remove all the account data in case the account
+       * is really removed from the panel.
+       */
+      datasource = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+      element = g_list_find_custom (job->acc_objects, datasource,
+                                    cleanup_datasource_compare);
+
+      if (element == NULL)
+        job->old_datasources = g_list_prepend (job->old_datasources,
+                                               g_strdup (datasource));
+    }
+
+  g_object_unref (cursor);
+
+  /* cleanup the DB */
+  cleanup_job_do_cleanup (job);
+
+ out:
+  g_io_scheduler_job_send_to_mainloop_async (sched_job,
+                                             cleanup_old_accounts_done, job, NULL);
+  return FALSE;
+}
+
+static void
+gd_zpj_miner_cleanup_old_accounts (GdZpjMiner *self,
+                                     GList *doc_objects,
+                                     GList *acc_objects)
+{
+  CleanupJob *job = g_slice_new0 (CleanupJob);
+
+  job->self = g_object_ref (self);
+  job->doc_objects = doc_objects;
+  job->acc_objects = acc_objects;
+
+  g_io_scheduler_push_job (cleanup_job, job, NULL,
+                           G_PRIORITY_DEFAULT,
+                           self->priv->cancellable);
+}
+
+static void
+client_ready_cb (GObject *source,
+                 GAsyncResult *res,
+                 gpointer user_data)
+{
+  GdZpjMiner *self = user_data;
+  GoaDocuments *documents;
+  GoaAccount *account;
+  GoaObject *object;
+  const gchar *provider_type;
+  GError *error = NULL;
+  GList *accounts, *doc_objects, *acc_objects, *l;
+
+  self->priv->client = goa_client_new_finish (res, &error);
+
+  if (error != NULL)
+    {
+      gd_zpj_miner_complete_error (self, error);
+      return;
+    }
+
+  doc_objects = NULL;
+  acc_objects = NULL;
+
+  accounts = goa_client_get_accounts (self->priv->client);
+  for (l = accounts; l != NULL; l = l->next)
+    {
+      object = l->data;
+
+      account = goa_object_peek_account (object);
+      if (account == NULL)
+        continue;
+
+      provider_type = goa_account_get_provider_type (account);
+      if (g_strcmp0 (provider_type, "windows_live") != 0)
+        continue;
+
+      acc_objects = g_list_append (acc_objects, g_object_ref (object));
+
+      documents = goa_object_peek_documents (object);
+      if (documents == NULL)
+        continue;
+
+      doc_objects = g_list_append (doc_objects, g_object_ref (object));
+    }
+
+  g_list_free_full (accounts, g_object_unref);
+
+  gd_zpj_miner_cleanup_old_accounts (self, doc_objects, acc_objects);
+}
+
+static void
+sparql_connection_ready_cb (GObject *object,
+                            GAsyncResult *res,
+                            gpointer user_data)
+{
+  GError *error = NULL;
+  GdZpjMiner *self = user_data;
+
+  self->priv->connection = tracker_sparql_connection_get_finish (res, &error);
+
+  if (error != NULL)
+    {
+      gd_zpj_miner_complete_error (self, error);
+      return;
+    }
+
+  goa_client_new (self->priv->cancellable, client_ready_cb, self);
+}
+
+static void
+gd_zpj_miner_dispose (GObject *object)
+{
+  GdZpjMiner *self = GD_ZPJ_MINER (object);
+
+  if (self->priv->pending_jobs != NULL)
+    {
+      g_list_free_full (self->priv->pending_jobs,
+                        (GDestroyNotify) account_miner_job_free);
+      self->priv->pending_jobs = NULL;
+    }
+
+  g_clear_object (&self->priv->client);
+  g_clear_object (&self->priv->connection);
+  g_clear_object (&self->priv->cancellable);
+  g_clear_object (&self->priv->result);
+
+  G_OBJECT_CLASS (gd_zpj_miner_parent_class)->dispose (object);
+}
+
+static void
+gd_zpj_miner_init (GdZpjMiner *self)
+{
+  self->priv =
+    G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_ZPJ_MINER, GdZpjMinerPrivate);
+}
+
+static void
+gd_zpj_miner_class_init (GdZpjMinerClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  oclass->dispose = gd_zpj_miner_dispose;
+
+  g_type_class_add_private (klass, sizeof (GdZpjMinerPrivate));
+}
+
+GdZpjMiner *
+gd_zpj_miner_new (void)
+{
+  return g_object_new (GD_TYPE_ZPJ_MINER, NULL);
+}
+
+void
+gd_zpj_miner_refresh_db_async (GdZpjMiner *self,
+                               GCancellable *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+  self->priv->result =
+    g_simple_async_result_new (G_OBJECT (self),
+                               callback, user_data,
+                               gd_zpj_miner_refresh_db_async);
+  self->priv->cancellable =
+    (cancellable != NULL) ? g_object_ref (cancellable) : NULL;
+
+  tracker_sparql_connection_get_async (self->priv->cancellable,
+                                       sparql_connection_ready_cb, self);
+}
+
+gboolean
+gd_zpj_miner_refresh_db_finish (GdZpjMiner *self,
+                                GAsyncResult *res,
+                                GError **error)
+{
+  GSimpleAsyncResult *simple_res = G_SIMPLE_ASYNC_RESULT (res);
+
+  g_assert (g_simple_async_result_is_valid (res, G_OBJECT (self),
+                                            gd_zpj_miner_refresh_db_async));
+
+  if (g_simple_async_result_propagate_error (simple_res, error))
+    return FALSE;
+
+  return TRUE;
+}
diff --git a/src/miner/gd-zpj-miner.h b/src/miner/gd-zpj-miner.h
new file mode 100644
index 0000000..20b0b2d
--- /dev/null
+++ b/src/miner/gd-zpj-miner.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012 Red Hat, Inc.
+ *
+ * Gnome Documents 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.
+ *
+ * Gnome Documents 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 Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#ifndef __GD_ZPJ_MINER_H__
+#define __GD_ZPJ_MINER_H__
+
+#include <libtracker-miner/tracker-miner.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_ZPJ_MINER gd_zpj_miner_get_type()
+
+#define GD_ZPJ_MINER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+   GD_TYPE_ZPJ_MINER, GdZpjMiner))
+
+#define GD_ZPJ_MINER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+   GD_TYPE_ZPJ_MINER, GdZpjMinerClass))
+
+#define GD_IS_ZPJ_MINER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+   GD_TYPE_ZPJ_MINER))
+
+#define GD_IS_ZPJ_MINER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+   GD_TYPE_ZPJ_MINER))
+
+#define GD_ZPJ_MINER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+   GD_TYPE_ZPJ_MINER, GdZpjMinerClass))
+
+typedef struct _GdZpjMiner GdZpjMiner;
+typedef struct _GdZpjMinerClass GdZpjMinerClass;
+typedef struct _GdZpjMinerPrivate GdZpjMinerPrivate;
+
+struct _GdZpjMiner {
+  GObject parent;
+
+  GdZpjMinerPrivate *priv;
+};
+
+struct _GdZpjMinerClass {
+  GObjectClass parent_class;
+};
+
+GType gd_zpj_miner_get_type(void);
+
+GdZpjMiner * gd_zpj_miner_new (void);
+
+void           gd_zpj_miner_refresh_db_async (GdZpjMiner *self,
+                                              GCancellable *cancellable,
+                                              GAsyncReadyCallback callback,
+                                              gpointer user_data);
+gboolean       gd_zpj_miner_refresh_db_finish (GdZpjMiner *self,
+                                               GAsyncResult *res,
+                                               GError **error);
+
+G_END_DECLS
+
+#endif /* __GD_ZPJ_MINER_H__ */
diff --git a/src/miner/zpj-miner-main.c b/src/miner/zpj-miner-main.c
new file mode 100644
index 0000000..e48f3c2
--- /dev/null
+++ b/src/miner/zpj-miner-main.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2012 Red Hat, Inc.
+ *
+ * Gnome Documents 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.
+ *
+ * Gnome Documents 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 Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Debarshi Ray <debarshir gnome org>
+ *
+ */
+
+#include <glib-unix.h>
+#include <glib.h>
+
+#include "gd-zpj-miner.h"
+
+#define BUS_NAME "org.gnome.Documents.ZpjMiner"
+#define AUTOQUIT_TIMEOUT 5 /* seconds */
+
+static const gchar introspection_xml[] =
+  "<node>"
+  "  <interface name='org.gnome.Documents.ZpjMiner'>"
+  "    <method name='RefreshDB'>"
+  "    </method>"
+  "  </interface>"
+  "</node>";
+
+static GDBusNodeInfo *introspection_data = NULL;
+static GCancellable *cancellable = NULL;
+static GMainLoop *loop = NULL;
+static guint name_owner_id = 0;
+static guint autoquit_id = 0;
+static gboolean refreshing = FALSE;
+
+static gboolean
+autoquit_timeout_cb (gpointer _unused)
+{
+  g_debug ("Timeout reached, quitting...");
+
+  autoquit_id = 0;
+  g_main_loop_quit (loop);
+
+  return FALSE;
+}
+
+static void
+ensure_autoquit_off (void)
+{
+  if (g_getenv ("ZPJ_MINER_PERSIST") != NULL)
+    return;
+
+  if (autoquit_id != 0)
+    {
+      g_source_remove (autoquit_id);
+      autoquit_id = 0;
+    }
+}
+
+static void
+ensure_autoquit_on (void)
+{
+  if (g_getenv ("ZPJ_MINER_PERSIST") != NULL)
+    return;
+
+  autoquit_id =
+    g_timeout_add_seconds (AUTOQUIT_TIMEOUT,
+                           autoquit_timeout_cb, NULL);
+}
+
+static gboolean
+signal_handler_cb (gpointer user_data)
+{
+  GMainLoop *loop = user_data;
+
+  if (cancellable != NULL)
+    g_cancellable_cancel (cancellable);
+
+  g_main_loop_quit (loop);
+
+  return FALSE;
+}
+
+static void
+miner_refresh_db_ready_cb (GObject *source,
+                           GAsyncResult *res,
+                           gpointer user_data)
+{
+  GDBusMethodInvocation *invocation = user_data;
+  GError *error = NULL;
+
+  gd_zpj_miner_refresh_db_finish (GD_ZPJ_MINER (source), res, &error);
+
+  refreshing = FALSE;
+  ensure_autoquit_on ();
+
+  if (error != NULL)
+    {
+      g_printerr ("Failed to refresh the DB cache: %s\n", error->message);
+      g_dbus_method_invocation_return_gerror (invocation, error);
+    }
+  else
+    {
+      g_dbus_method_invocation_return_value (invocation, NULL);
+    }
+}
+
+static void
+handle_refresh_db (GDBusMethodInvocation *invocation)
+{
+  GdZpjMiner *miner;
+
+  ensure_autoquit_off ();
+
+  /* if we're refreshing already, compress with the current request */
+  if (refreshing)
+    return;
+
+  refreshing = TRUE;
+  cancellable = g_cancellable_new ();
+  miner = gd_zpj_miner_new ();
+
+  gd_zpj_miner_refresh_db_async (miner, cancellable,
+                                 miner_refresh_db_ready_cb, invocation);
+
+  g_object_unref (miner);
+}
+
+static void
+handle_method_call (GDBusConnection       *connection,
+                    const gchar           *sender,
+                    const gchar           *object_path,
+                    const gchar           *interface_name,
+                    const gchar           *method_name,
+                    GVariant              *parameters,
+                    GDBusMethodInvocation *invocation,
+                    gpointer               user_data)
+{
+  if (g_strcmp0 (method_name, "RefreshDB") == 0)
+    handle_refresh_db (g_object_ref (invocation));
+  else
+    g_assert_not_reached ();
+}
+
+static const GDBusInterfaceVTable interface_vtable =
+{
+  handle_method_call,
+  NULL, /* get_property */
+  NULL, /* set_property */
+};
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+                 const gchar *name,
+                 gpointer user_data)
+{
+  GError *error = NULL;
+
+  g_debug ("Connected to the session bus: %s", name);
+
+  g_dbus_connection_register_object (connection,
+                                     "/org/gnome/Documents/ZpjMiner",
+                                     introspection_data->interfaces[0],
+                                     &interface_vtable,
+                                     NULL,
+                                     NULL,
+                                     &error);
+
+  if (error != NULL)
+    {
+      g_printerr ("Error exporting object on the session bus: %s",
+                  error->message);
+      g_error_free (error);
+
+      _exit (1);
+    }
+
+  g_debug ("Object exported on the session bus");
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+              const gchar *name,
+              gpointer user_data)
+{
+  g_debug ("Lost bus name: %s, exiting", name);
+
+  if (cancellable != NULL)
+    g_cancellable_cancel (cancellable);
+
+  name_owner_id = 0;
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+                  const gchar *name,
+                  gpointer user_data)
+{
+  g_debug ("Acquired bus name: %s", name);
+}
+
+int
+main (int argc,
+      char **argv)
+{
+  g_type_init ();
+
+  ensure_autoquit_on ();
+  loop = g_main_loop_new (NULL, FALSE);
+
+  g_unix_signal_add_full (G_PRIORITY_DEFAULT,
+			  SIGTERM,
+			  signal_handler_cb,
+			  loop, NULL);
+  g_unix_signal_add_full (G_PRIORITY_DEFAULT,
+			  SIGINT,
+			  signal_handler_cb,
+			  loop, NULL);
+
+  introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+  g_assert (introspection_data != NULL);
+
+  name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+                                  BUS_NAME,
+                                  G_BUS_NAME_OWNER_FLAGS_NONE,
+                                  on_bus_acquired,
+                                  on_name_acquired,
+                                  on_name_lost,
+                                  NULL, NULL);
+
+  g_main_loop_run (loop);
+  g_main_loop_unref (loop);
+
+  if (name_owner_id != 0)
+    g_bus_unown_name (name_owner_id);
+
+  return 0;
+}



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