[PATCH 01/13] cache: Add cache subsystem
- From: "Juan A. Suarez Romero" <jasuarez igalia com>
- To: grilo-list gnome org
- Subject: [PATCH 01/13] cache: Add cache subsystem
- Date: Tue, 15 Feb 2011 19:07:07 +0100
This cache subsystem allows both applications and plugins to save and recover
later GrlMedia content.
Signed-off-by: Juan A. Suarez Romero <jasuarez igalia com>
---
configure.ac | 3 +-
src/Makefile.am | 4 +-
src/grilo.h | 1 +
src/grl-error.h | 4 +-
src/grl-media-cache.c | 446 +++++++++++++++++++++++++++++++++++++++++++++++++
src/grl-media-cache.h | 106 ++++++++++++
6 files changed, 561 insertions(+), 3 deletions(-)
create mode 100644 src/grl-media-cache.c
create mode 100644 src/grl-media-cache.h
diff --git a/configure.ac b/configure.ac
index 9999282..8cfa20c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -93,7 +93,8 @@ PKG_CHECK_MODULES(DEPS, glib-2.0 >= 2.22 \
gobject-2.0 \
gmodule-2.0 \
gio-2.0 \
- libxml-2.0)
+ libxml-2.0 \
+ sqlite3)
GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
GLIB_MKENUMS=`pkg-config --variable=glib_mkenums glib-2.0`
diff --git a/src/Makefile.am b/src/Makefile.am
index e5b82cc..3a5183b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,6 +47,7 @@ lib@GRL_NAME@_la_SOURCES = \
grl-multiple.c \
grl-log.c grl-log-priv.h \
grl-sync.c \
+ grl-media-cache.c \
grilo.c
data_c_sources = \
@@ -74,7 +75,8 @@ lib@GRL_NAME@inc_HEADERS = \
grl-log.h \
grl-multiple.h \
grl-util.h \
- grl-definitions.h
+ grl-definitions.h \
+ grl-media-cache.h
data_h_headers = \
data/grl-data.h \
diff --git a/src/grilo.h b/src/grilo.h
index cff47a0..e659ec4 100644
--- a/src/grilo.h
+++ b/src/grilo.h
@@ -32,6 +32,7 @@
#include <grl-plugin-registry.h>
#include <grl-media-plugin.h>
#include <grl-media-source.h>
+#include <grl-media-cache.h>
#include <grl-metadata-source.h>
#include <grl-metadata-key.h>
#include <grl-data.h>
diff --git a/src/grl-error.h b/src/grl-error.h
index 1480857..f2b431c 100644
--- a/src/grl-error.h
+++ b/src/grl-error.h
@@ -57,6 +57,7 @@
* @GRL_CORE_ERROR_REGISTER_METADATA_KEY_FAILED: Failed to register metadata key
* @GRL_CORE_ERROR_NOTIFY_CHANGED_FAILED: Failed to start changed notifications
* @GRL_CORE_ERROR_OPERATION_CANCELLED: The operation was cancelled
+ * @GRL_CORE_ERROR_CACHE_FAILED: An operation on the cache has failed
*
* These constants identify all the available core errors
*/
@@ -79,7 +80,8 @@ typedef enum {
GRL_CORE_ERROR_UNLOAD_PLUGIN_FAILED,
GRL_CORE_ERROR_REGISTER_METADATA_KEY_FAILED,
GRL_CORE_ERROR_NOTIFY_CHANGED_FAILED,
- GRL_CORE_ERROR_OPERATION_CANCELLED
+ GRL_CORE_ERROR_OPERATION_CANCELLED,
+ GRL_CORE_ERROR_CACHE_FAILED,
} GrlCoreError;
#endif /* _GRL_ERROR_H_ */
diff --git a/src/grl-media-cache.c b/src/grl-media-cache.c
new file mode 100644
index 0000000..c210d97
--- /dev/null
+++ b/src/grl-media-cache.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**
+ * SECTION:grl-media_cache
+ * @short_description: GrlMedia Cache provider
+ * @see_also: #GrlMedia
+ *
+ * GrlMediaCache subsystem provides a caching backstore to both plugins and
+ * clients, where they can cache the #GrlMedia and then recover them for future
+ * use.
+ */
+
+#include <sqlite3.h>
+#include <string.h>
+
+#include "grl-error.h"
+#include "grl-log.h"
+#include "grl-media-cache.h"
+
+#define GRL_LOG_DOMAIN_DEFAULT media_cache_log_domain
+GRL_LOG_DOMAIN(media_cache_log_domain);
+
+#define GRL_MEDIA_CACHE_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((object), \
+ GRL_TYPE_MEDIA_CACHE, \
+ GrlMediaCachePrivate))
+
+#define GRL_CACHE_DB ".grl-cache"
+
+#define GRL_CACHE_PATTERN "cache_%u"
+
+#define GRL_CACHE_CREATE_CACHE \
+ "CREATE TABLE %s (" \
+ "id TEXT PRIMARY KEY, " \
+ "parent TEXT REFERENCES %s (id), " \
+ "updated DATE, " \
+ "media TEXT)"
+
+#define GRL_CACHE_REMOVE_CACHE \
+ "DROP TABLE %s"
+
+#define GRL_CACHE_INSERT_ELEMENT \
+ "INSERT OR REPLACE INTO %s " \
+ "(id, parent, updated, media) VALUES " \
+ "(?, ?, ?, ?)"
+
+#define GRL_CACHE_GET_ELEMENT \
+ "SELECT parent, updated, media from %s " \
+ "WHERE id='%s' "
+
+enum {
+ PROP_0,
+ PROP_CACHE_ID,
+ PROP_PERSISTENT,
+};
+
+struct _GrlMediaCachePrivate {
+ gchar *cache_id;
+ gboolean persistent;
+ sqlite3 *db;
+};
+
+static void grl_media_cache_finalize (GObject *object);
+
+static void grl_media_cache_dispose (GObject *object);
+
+static void grl_media_cache_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void grl_media_cache_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void remove_table (sqlite3 *db,
+ const gchar *name);
+
+/* ================ GrlMediaCache GObject ================ */
+
+static void
+grl_media_cache_class_init (GrlMediaCacheClass *cache_class)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (cache_class);
+
+ gobject_class->finalize = grl_media_cache_finalize;
+ gobject_class->dispose = grl_media_cache_dispose;
+
+ gobject_class->set_property = grl_media_cache_set_property;
+ gobject_class->get_property = grl_media_cache_get_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_CACHE_ID,
+ g_param_spec_string ("cache-id",
+ "Cache identifier",
+ "Cache identifier",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class,
+ PROP_PERSISTENT,
+ g_param_spec_boolean ("persistent",
+ "persistent",
+ "Cache is persistent",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_type_class_add_private (cache_class,
+ sizeof (GrlMediaCachePrivate));
+}
+
+G_DEFINE_TYPE (GrlMediaCache, grl_media_cache, G_TYPE_OBJECT);
+
+static void
+grl_media_cache_init (GrlMediaCache *cache)
+{
+ cache->priv = GRL_MEDIA_CACHE_GET_PRIVATE (cache);
+}
+
+static void
+grl_media_cache_dispose (GObject *object)
+{
+ /* Unref all gobject elements */
+ /* Careful: it can be invoked several times */
+}
+
+static void
+grl_media_cache_finalize (GObject *object)
+{
+ GrlMediaCache *cache = GRL_MEDIA_CACHE (object);
+
+ /* Free all non-gobject elements */
+ GRL_DEBUG (__FUNCTION__);
+ if (!cache->priv->persistent) {
+ remove_table (cache->priv->db, cache->priv->cache_id);
+ }
+ sqlite3_close (cache->priv->db);
+ g_free (cache->priv->cache_id);
+
+ G_OBJECT_CLASS (grl_media_cache_parent_class)->finalize (object);
+}
+
+static void
+grl_media_cache_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GrlMediaCache *cache = GRL_MEDIA_CACHE (object);
+
+ GRL_DEBUG (__FUNCTION__);
+ switch (prop_id) {
+ case PROP_CACHE_ID:
+ cache->priv->cache_id = g_value_dup_string (value);
+ break;
+ case PROP_PERSISTENT:
+ cache->priv->persistent = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (cache, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+grl_media_cache_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GrlMediaCache *cache = GRL_MEDIA_CACHE (object);
+
+ GRL_DEBUG (__FUNCTION__);
+ switch (prop_id) {
+ case PROP_CACHE_ID:
+ g_value_set_string (value, cache->priv->cache_id);
+ break;
+ case PROP_PERSISTENT:
+ g_value_set_boolean (value, cache->priv->persistent);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (cache, prop_id, pspec);
+ break;
+ }
+}
+
+/* ================ Utilities ================ */
+
+static sqlite3 *
+create_table (const gchar *name)
+{
+ const gchar *home;
+ gchar *db_path;
+ gchar *sql_error = NULL;
+ gchar *sql_sentence;
+ sqlite3 *db;
+
+ home = g_getenv ("HOME");
+ if (!home) {
+ GRL_WARNING ("$HOME not set, cannot open database");
+ return NULL;
+ }
+
+ GRL_DEBUG ("Opening database connection...");
+ db_path = g_strconcat (home, G_DIR_SEPARATOR_S, GRL_CACHE_DB, NULL);
+ if (sqlite3_open (db_path, &db) != SQLITE_OK) {
+ g_critical ("Failed ot open database '%s': %s",
+ db_path,
+ sqlite3_errmsg (db));
+ g_free (db_path);
+ sqlite3_close (db);
+ return NULL;
+ }
+
+ g_free (db_path);
+
+ /* Create the table */
+ sql_sentence = g_strdup_printf (GRL_CACHE_CREATE_CACHE,
+ name, name);
+ if (sqlite3_exec (db, sql_sentence, NULL, NULL, &sql_error) != SQLITE_OK) {
+ if (sql_error) {
+ GRL_WARNING ("Failed to create cache '%s': %s", name, sql_error);
+ sqlite3_free (sql_error);
+ } else {
+ GRL_WARNING ("Failed to create cache '%s'", name);
+ }
+ g_free (sql_sentence);
+ sqlite3_close (db);
+ return NULL;
+ }
+
+ return db;
+}
+
+static void
+remove_table (sqlite3 *db,
+ const gchar *name)
+{
+ gchar *sql_error = NULL;
+ gchar *sql_sentence;
+
+ sql_sentence = g_strdup_printf (GRL_CACHE_REMOVE_CACHE, name);
+ if (sqlite3_exec (db, sql_sentence, NULL, NULL, &sql_error) != SQLITE_OK) {
+ if (sql_error) {
+ GRL_WARNING ("Failed to remove cache '%s': %s", name, sql_error);
+ sqlite3_free (sql_error);
+ } else {
+ GRL_WARNING ("Failed to remove cache '%s'", name);
+ }
+ }
+
+ g_free (sql_sentence);
+}
+
+/* ================ API ================ */
+
+
+GrlMediaCache *
+grl_media_cache_new (void)
+{
+ GrlMediaCache *cache = NULL;
+ gchar *cache_id;
+ sqlite3 *db;
+
+ GRL_DEBUG (__FUNCTION__);
+
+ /* Get a name */
+ cache_id = g_strdup_printf (GRL_CACHE_PATTERN, g_random_int ());
+
+ /* Create the cache */
+ db = create_table (cache_id);
+
+ if (db) {
+ cache = g_object_new (GRL_TYPE_MEDIA_CACHE,
+ "cache-id", cache_id,
+ NULL);
+ cache->priv->db = db;
+ }
+
+ g_free (cache_id);
+
+ return cache;
+}
+
+gboolean
+grl_media_cache_insert_media (GrlMediaCache *cache,
+ GrlMedia *media,
+ const gchar *parent,
+ GError **error)
+{
+ GTimeVal now;
+ gchar *now_str;
+ gchar *serial_media;
+ gchar *sql_sentence;
+ gint r;
+ sqlite3_stmt *sql_stmt;
+
+ g_return_val_if_fail (GRL_IS_MEDIA_CACHE (cache), FALSE);
+ g_return_val_if_fail (GRL_IS_MEDIA (media), FALSE);
+
+ GRL_DEBUG (__FUNCTION__);
+
+ /* Prepare the sentence */
+ sql_sentence = g_strdup_printf (GRL_CACHE_INSERT_ELEMENT,
+ cache->priv->cache_id);
+
+ if (sqlite3_prepare_v2 (cache->priv->db,
+ sql_sentence,
+ strlen (sql_sentence),
+ &sql_stmt, NULL) != SQLITE_OK) {
+ g_free (sql_sentence);
+ goto error;
+ }
+ g_free (sql_sentence);
+
+ /* Insert the values */
+ serial_media = grl_media_serialize_extended (media, GRL_MEDIA_SERIALIZE_FULL);
+ g_get_current_time (&now);
+ now_str = g_time_val_to_iso8601 (&now);
+
+ sqlite3_bind_text (sql_stmt, 1, grl_media_get_id (media), -1, SQLITE_STATIC);
+ sqlite3_bind_text (sql_stmt, 2, parent, -1, SQLITE_STATIC);
+ sqlite3_bind_text (sql_stmt, 3, now_str, -1, SQLITE_TRANSIENT);
+ sqlite3_bind_text (sql_stmt, 4, serial_media, -1, SQLITE_TRANSIENT);
+
+ g_free (serial_media);
+ g_free (now_str);
+
+ /* Wait until it finish */
+ while ((r = sqlite3_step (sql_stmt)) == SQLITE_BUSY);
+
+ if (r != SQLITE_DONE) {
+ sqlite3_finalize (sql_stmt);
+ goto error;
+ }
+
+ sqlite3_finalize (sql_stmt);
+
+ return TRUE;
+
+ error:
+ GRL_WARNING ("Failed to cache media in '%s': %s",
+ cache->priv->cache_id,
+ sqlite3_errmsg (cache->priv->db));
+ g_set_error (error,
+ GRL_CORE_ERROR,
+ GRL_CORE_ERROR_CACHE_FAILED,
+ "Failed to cache media in '%s'",
+ cache->priv->cache_id);
+ return FALSE;
+}
+
+GrlMedia *
+grl_media_cache_get_media (GrlMediaCache *cache,
+ const gchar *media_id,
+ gchar **parent,
+ GTimeVal *last_time_changed,
+ GError **error)
+{
+ GrlMedia *media;
+ gchar *sql_sentence;
+ gint r;
+ sqlite3_stmt *sql_stmt;
+
+ g_return_val_if_fail (GRL_IS_MEDIA_CACHE (cache), NULL);
+ g_return_val_if_fail (media_id, NULL);
+
+ GRL_DEBUG (__FUNCTION__);
+
+ sql_sentence = g_strdup_printf (GRL_CACHE_GET_ELEMENT,
+ cache->priv->cache_id,
+ media_id);
+
+ if (sqlite3_prepare_v2 (cache->priv->db,
+ sql_sentence,
+ strlen (sql_sentence),
+ &sql_stmt,
+ NULL) != SQLITE_OK) {
+ g_free (sql_sentence);
+ goto error;
+ };
+
+ g_free (sql_sentence);
+
+ while ((r = sqlite3_step (sql_stmt)) == SQLITE_BUSY);
+
+ if (r != SQLITE_ROW) {
+ sqlite3_finalize (sql_stmt);
+ goto error;
+ }
+
+ if (parent) {
+ *parent = g_strdup ((const gchar *) sqlite3_column_text (sql_stmt, 0));
+ }
+
+ if (last_time_changed) {
+ g_time_val_from_iso8601 ((const gchar *) sqlite3_column_text (sql_stmt, 1),
+ last_time_changed);
+ }
+
+ media =
+ grl_media_unserialize ((const gchar *) sqlite3_column_text (sql_stmt, 2));
+
+ sqlite3_finalize (sql_stmt);
+
+ return media;
+
+ error:
+ GRL_WARNING ("Failed to get media '%s' from '%s': %s",
+ media_id,
+ cache->priv->cache_id,
+ sqlite3_errmsg (cache->priv->db));
+ g_set_error (error,
+ GRL_CORE_ERROR,
+ GRL_CORE_ERROR_CACHE_FAILED,
+ "Failed to get media '%s' from '%s'",
+ media_id,
+ cache->priv->cache_id);
+ return FALSE;
+}
diff --git a/src/grl-media-cache.h b/src/grl-media-cache.h
new file mode 100644
index 0000000..6b427d5
--- /dev/null
+++ b/src/grl-media-cache.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#if !defined (_GRILO_H_INSIDE_) && !defined (GRILO_COMPILATION)
+#error "Only <grilo.h> can be included directly."
+#endif
+
+#ifndef _GRL_MEDIA_CACHE_H_
+#define _GRL_MEDIA_CACHE_H_
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "grl-definitions.h"
+#include "grl-media.h"
+
+/* Macros */
+
+#define GRL_TYPE_MEDIA_CACHE \
+ (grl_media_cache_get_type ())
+
+#define GRL_MEDIA_CACHE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ GRL_TYPE_MEDIA_CACHE, \
+ GrlMediaCache))
+
+#define GRL_IS_MEDIA_CACHE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ GRL_TYPE_MEDIA_CACHE))
+
+#define GRL_MEDIA_CACHE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), \
+ GRL_TYPE_MEDIA_CACHE, \
+ GrlMediaCacheClass))
+
+#define GRL_IS_MEDIA_CACHE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), \
+ GRL_TYPE_MEDIA_CACHE))
+
+#define GRL_MEDIA_CACHE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ GRL_TYPE_MEDIA_CACHE, \
+ GrlMediaCacheClass))
+
+/* GrlMediaCache object */
+
+typedef struct _GrlMediaCache GrlMediaCache;
+typedef struct _GrlMediaCachePrivate GrlMediaCachePrivate;
+typedef struct _GrlMediaCacheClass GrlMediaCacheClass;
+
+struct _GrlMediaCache {
+
+ GObject parent;
+
+ /*< private >*/
+ GrlMediaCachePrivate *priv;
+
+ gpointer _grl_reserved[GRL_PADDING];
+};
+
+struct _GrlMediaCacheClass {
+ GObjectClass parent_class;
+
+ /*< private >*/
+ gpointer _grl_reserved[GRL_PADDING];
+};
+
+G_BEGIN_DECLS
+
+GType grl_media_cache_get_type (void);
+
+GrlMediaCache *grl_media_cache_new (void);
+
+gboolean grl_media_cache_insert_media (GrlMediaCache *cache,
+ GrlMedia *media,
+ const gchar *parent,
+ GError **error);
+
+GrlMedia *grl_media_cache_get_media (GrlMediaCache *cache,
+ const gchar *media_id,
+ gchar **parent,
+ GTimeVal *last_time_changed,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _GRL_MEDIA_CACHE_H_ */
--
1.7.4
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]